Question

I've started to build a tcp server which will be able to accept many clients, and receive simultaneously from all of the clients new data.

Until now, I used IOCP for tcp servers which was pretty easy and comfortable, but this time I want to use the Async / Await tech. that was released in C# 5.0.

The problem is that when I started to write the server using async / await, I figured out that in tcp multiple users server use case, async / await tech. and the regular synchrony methods will work the same.

Here's a simple example to be more specific:

class Server
{
    private TcpListener _tcpListener;
    private List<TcpClient> _clients;
    private bool IsStarted;

    public Server(int port)
    {
        _tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, port));
        _clients = new List<TcpClient>();
        IsStarted = false;
    }

    public void Start()
    {
        IsStarted = true;
        _tcpListener.Start();
        Task.Run(() => StartAcceptClientsAsync());
    }

    public void Stop()
    {
        IsStarted = false;
        _tcpListener.Stop();
    }

    private async Task StartAcceptClientsAsync()
    {
        while (IsStarted)
        {
            // ******** Note 1 ********
            var acceptedClient = await _tcpListener.AcceptTcpClientAsync();

            _clients.Add(acceptedClient);
            IPEndPoint ipEndPoint = (IPEndPoint) acceptedClient.Client.RemoteEndPoint;
            Console.WriteLine("Accepted new client! IP: {0} Port: {1}", ipEndPoint.Address, ipEndPoint.Port);

            Task.Run(() => StartReadingDataFromClient(acceptedClient));
        }
    }

    private async void StartReadingDataFromClient(TcpClient acceptedClient)
    {
        try
        {
            IPEndPoint ipEndPoint = (IPEndPoint) acceptedClient.Client.RemoteEndPoint;
            while (true)
            {
                MemoryStream bufferStream = new MemoryStream();
                // ******** Note 2 ********
                byte[] buffer = new byte[1024];
                int packetSize = await acceptedClient.GetStream().ReadAsync(buffer, 0, buffer.Length);

                if (packetSize == 0)
                {
                    break;
                }

                Console.WriteLine("Accepted new message from: IP: {0} Port: {1}\nMessage: {2}",
                    ipEndPoint.Address, ipEndPoint.Port, Encoding.Default.GetString(buffer));
            }
        }
        catch (Exception)
        { 
        }
        finally
        {
            acceptedClient.Close();
            _clients.Remove(acceptedClient);
        }
    }
}

Now if you see the lines under 'Note 1' and 'Note 2', It can easily be changed to:

Note 1 from

var acceptedClient = await _tcpListener.AcceptTcpClientAsync();

to

var acceptedClient = _tcpListener.AcceptTcpClient();

And Note 2 from

int packetSize = await acceptedClient.GetStream().ReadAsync(buffer, 0, 1024);

to

int packetSize = acceptedClient.GetStream().Read(buffer, 0, 1024);

And the server will work exactly the same.

So, why using async / await in tcp listener for multiple users if it's the same like using regular synchrony methods?

Should I keep using IOCP in that case? because for me it's pretty easy and comfortable but I am afraid that it will be obsoleted or even no more available in newer .NET versions.

Was it helpful?

Solution

Untill now, i used IOCP for tcp servers which was pretty easy and comfortable, but this time i want to use the Async / Await tech. that was released in C# 5.0.

I think you need to get your terminology right.

Having BeginOperation and EndOperation methods is called Asynchronous Programming Model (APM). Having a single Task (or Task<T>) returning method is called Task-based Asynchronous Pattern (TAP). I/O Completion Ports (IOCP) are a way to handle asynchronous operations on Windows and asynchronous I/O methods using both APM and TAP use them.

What this means is that the performance of APM and TAP is going to be very similar. The big difference between the two is that code using TAP and async-await is much more readable than code using APM and callbacks.

So, if you want to (or have to) write your code asynchronously, use TAP and async-await, if you can. But if you don't have a good reason to do that, just write your code synchronously.

On the server, a good reason to use asynchrony is scalability: asynchronous code can handle many more requests at the same time, because it tends to use fewer threads. If you don't care about scalability (for example because you're not going to have many users at the same time), then asynchrony doesn't make much sense.


Also, your code contains some practices that you should avoid:

  • Don't use async void methods, there is no good way to tell when they complete and they have bad exception handling. The exception is event handlers, but that applies mostly to GUI applications.
  • Don't use Task.Run() if you don't have to. Task.Run() can be useful if you want to leave the current thread (usually the UI thread) or if you want to execute synchronous code in parallel. But it doesn't make much sense to use it to start asynchronous operations in server applications.
  • Don't ignore Tasks returned from methods, unless you're sure they're not going to throw an exception. Exceptions from ignored Tasks won't do anything, which could very easily mask a bug.

OTHER TIPS

After couple of search i found this

Q: It is a list of TCPServer practices but which one is the best practice for managing 5000+ clients each seconds ?

A: my assumption is "Writing async methods" more over if you are working with database same time "Async methods and iterators" will do more

here is the sample code using async/await.

async await tcp server

I think it is easier to build a tcp server using async/await than iocp.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top