Skip to content

Networking Getting started

Alexander edited this page Aug 26, 2022 · 8 revisions

TCP

First of all we need to create our custom server/client/connection. It's necessary to take control over a connection.

Let's make a simple chat

public class MyTcpServer : TcpServer
{
    public MyTcpServer(TcpConfigurationServer configuration) : base(configuration)
    {
    }

    protected override TcpConnection CreateConnection()
    {
        return new MyTcpConnection(this);
    }
}
public class MyTcpClient : TcpClient
{
    public MyTcpClient(TcpConfigurationClient configuration) : base(configuration)
    {
    }

    protected override TcpConnection CreateConnection()
    {
        return new MyTcpConnection(this);
    }
}
public class MyTcpConnection : TcpConnection
{
    public MyTcpConnection(TcpPeer peer) : base(peer)
    {
    }

    protected override void OnMessageReceived(MessageEventArgs args)
    {
        using (args)
        {
            string nickname = args.Message.ReadString();
            string message = args.Message.ReadString();
            logger.Info($"[{Parent}] [RECEIVED] {nickname}: {message}");

            if (!IsClientConnection)
                _ = SendChatMessage("Server", "Message accepted");
        }
        base.OnMessageReceived(args);
    }

    public async Task SendChatMessage(string nickname, string message)
    {
        using (RawMessage newMsg = Parent.CreateMessage())
        {
            newMsg.Write(nickname);
            newMsg.Write(message);
            await this.SendMessageAsync(newMsg);
            logger.Info($"[{Parent}] [SENT] {nickname}: {message}");
        }
    }
}

You may ask the question "Why is sending a message is asynchronous operation, but receiving is not". The answer is performance. When we receiving a message we are striving to make the network thread ASAP, to get the next message, but sending is a blocking operation, we don't want anything to block the receiving thread. Sending a message may take some (especially if connection simulation is enabled), so it's asynchronous.

Remember, you're always responsible for disposing incoming and outgoing messages. If you won't do it, it will make additional stress on GC

Let's create client/server and make a test

TcpConfigurationServer configurationServer = new TcpConfigurationServer();

configurationServer.LogManager = new LogManager(LogSeverity.INFO, 
     new LoggingHandlerConsole(new LoggingFormatterDefault()));

MyTcpServer server = new MyTcpServer(configurationServer);
server.Start();
server.Listen(10000); //Listen is a non blocking method

TcpConfigurationClient configurationClient = new TcpConfigurationClient();
configurationClient.LogManager = new LogManager(LogSeverity.INFO, 
     new LoggingHandlerConsole(new LoggingFormatterDefault()));

MyTcpClient client = new MyTcpClient(configurationClient);
client.Start();

await client.ConnectAsync("127.0.0.1", 10000);
await (client.Connection as MyTcpConnection)?.SendChatMessage("Client", "Hello")!;

so output:

[2022-08-25 17:38:47.587] [TcpClient] [INFO] TcpClient status changed from Disconnected to Connecting
[2022-08-25 17:38:47.616] [TcpClient] [INFO] Starting the connection to 127.0.0.1:10000
[2022-08-25 17:38:47.669] [TcpConnection] [INFO] #1 initialized
[2022-08-25 17:38:47.669] [TcpConnection] [INFO] #1 initialized
[2022-08-25 17:38:47.678] [TcpClient] [INFO] TcpClient status changed from Connecting to Connected
[2022-08-25 17:38:47.779] [TcpConnection] [INFO] [Neon.Test.Tcp.MyTcpClient] [SENT] Client: Hello
[2022-08-25 17:38:47.787] [TcpConnection] [INFO] [Neon.Test.Tcp.MyTcpServer] [RECEIVED] Client: Hello
[2022-08-25 17:38:47.792] [TcpConnection] [INFO] [Neon.Test.Tcp.MyTcpServer] [SENT] Server: Message accepted
[2022-08-25 17:38:47.794] [TcpConnection] [INFO] [Neon.Test.Tcp.MyTcpClient] [RECEIVED] Server: Message accepted

Callbacks / events

TcpClient, TcpServer, UdpClient, UdpServer, TcpConnection, UdpConnection have protected virtual method to create a connection and control it's state

  • CreateConnection - this method is not synchronized with a context
  • OnConnectionOpened - this method is synchronized with a context
  • OnConnectionClosed - this method is synchronized with a context

TcpClient, UdpClient have status change protected virtual method

  • OnClientStatusChanged - this method is synchronized with a context

TcpServer, UdpServer have method to filter incoming connections

  • OnAcceptConnection - if method returns false connection will be dropped before opening. This method is not synchronized with a context. Returns true by default

All the calls of events and RPC methods are synchronized with your context (if set), see more

UDP

UDP works very similar to TCP, it just gives you some options how to send messages

DeliveryType

  • Unreliable - the other side may not receive the message, or receive it multiple times.
  • UnreliableSequenced - every message have sequence number increased by 1 every time. The other side will drop any messages where sequence number less or equals the last one. This method could help you to get rid of old and duplicate messages. Game servers often use it for movement synchronization.
  • ReliableUnordered - the channel with reliable delivery, but messages may come in different order.
  • ReliableOrdered - this delivery method completely repeats TCP, messages come reliably in the same order

Channels

Every delivery type has 7 channels (0-7) to split traffic. For example UnreliableSequenced messages will have different sequences in different channels.

Detailed configuration

TCP Configuration

UDP Configuration

Clone this wiki locally