Asynchronous TCP network server with a packet system

Posted on

Problem

So I’m writing a game emulator and would like some advice after finishing the networking. It is designed to accept multiple connections, and process messages from all of them.

Here is just a brief introduction to the packets and how they are structured. [lengthOfString:short][stringEncodedInUtf8:Byte[]]

Let’s start with the NetworkHandler – this class is responsible for accepting new connections and storing them in the collection.

public class NetworkHandler : IDisposable
{
    private readonly TcpListener _listener;
    private readonly IList<NetworkClient> _clients;
    private readonly ClientPacketHandler _packetHandler;

    public NetworkHandler(TcpListener listener, IList<NetworkClient> clients, ClientPacketHandler packetHandler)
    {
        _listener = listener;
        _clients = clients;
        _packetHandler = packetHandler;
    }

    public void StartListener()
    {
        _listener.Start();
    }

    public async Task ListenAsync()
    {
        while (true)
        {
            var tcpClient = await _listener.AcceptTcpClientAsync();
            var networkClient = new NetworkClient(tcpClient, _packetHandler);

            _clients.Add(networkClient);

            networkClient.StartReceiving();
        }
    }

    public void Dispose()
    {
        foreach (var client in _clients)
        {
            client.Dispose();
        }

        _listener.Stop();
    }
}

Then we have the NetworkClient, I made this so NetworkHandler could stay small and to follow SRP – This class handles incoming data from the individual connection (client).

public class NetworkClient
{
private readonly TcpClient _tcpClient;
private readonly NetworkStream _networkStream;
private readonly ClientPacketHandler _packetHandler;

public NetworkClient(TcpClient tcpClient, ClientPacketHandler packetHandler)
{
_tcpClient = tcpClient;
_networkStream = tcpClient.GetStream();
_packetHandler = packetHandler;
}

public void StartReceiving()
{
Task.Run(ProcessDataAsync);
}

private async Task ProcessDataAsync()
{
while (true)
{
using var br = new BinaryReader(new MemoryStream(await GetBinaryDataAsync()));
var messageLength = BinaryPrimitives.ReadInt32BigEndian(br.ReadBytes(4));
var packetData = br.ReadBytes(messageLength);

using var br2 = new BinaryReader(new MemoryStream(packetData));
var packetId = BinaryPrimitives.ReadInt16BigEndian(br2.ReadBytes(2));

if (packetId == 26979)
{
await WriteToStreamAsync(Encoding.Default.GetBytes("<?xml version="1.0""?>

Solution

rn<

!DOCTYPE cross-domain-policy SYSTEM ""/xml/dtds/cross-domain-policy.dtd"">

rn<

cross-domain-policy>

rn<

Leave a Reply

Your email address will not be published. Required fields are marked *