TCP servers with .NET: Scenario 4 & Non-blocking servers with .NET 4.5

Submitted by Robert MacLean on Mon, 11/21/2011 - 08:25

This post is part of a series, to see the other posts in the series go to the series index.

imageScenario 3 is a great solution to the problem, but the problem with it was the complexity of the code is way higher than in scenario 1 or scenario 2. We are calling to other methods, using recursive like behaviours and all kinds of things that are not obvious or easy to grasp.

Thankfully Microsoft has identified this issue and in .NET 4.5 we will be getting some help with this thanks to the new ASYNC features, in particular the async & await keywords.

In short async provides information to the compiler that this method will spawn off an asynchronous process and will return to the caller at some point while the await points out the line of code that line asynchronously.

So using this allowed me to modify the code back to pretty close to the scenario 1 code (I even have the Boolean continue running server flag). However the two methods are enhanced with the async keyword and in there the await keyword is used with AcceptTcpClientAsync  and ReadAsync – these are new methods in the TCP stack which return Task<T>, a fundamental requirement of calls that work with async.

I think this is a fantastic improvement, and I cannot wait for .NET 4.5 to ship Smile

class Program
{
    private const int BufferSize = 4096;
    private static bool ServerRunning = true;

    static void Main(string[] args)
    {
        var tcpServer = new TcpListener(IPAddress.Any, 9000);
        try
        {
            tcpServer.Start();

            ListenForClients(tcpServer);

            Console.WriteLine("Press enter to shutdown");
            Console.ReadLine();

        }
        finally
        {
            tcpServer.Stop();
        }
    }

    private static async void ListenForClients(TcpListener tcpServer)
    {
        while (ServerRunning)
        {
            var tcpClient = await tcpServer.AcceptTcpClientAsync();
            Console.WriteLine("Connected");
            ProcessClient(tcpClient);
        }
    }

    private static async void ProcessClient(TcpClient tcpClient)
    {
        while (ServerRunning)
        {
            var stream = tcpClient.GetStream();
            var buffer = new byte[BufferSize];
            var amountRead = await stream.ReadAsync(buffer, 0, BufferSize);

            var message = Encoding.ASCII.GetString(buffer, 0, amountRead);
            Console.WriteLine("Client sent: {0}", message);
        }
    }

}