How to do non-blocking TCP comms?

Rhodan

Active member
Joined
Apr 7, 2015
Messages
41
Programming Experience
10+
I'm playing with async TCP to communicate between two applications. I'm having a heck of a time trying to figure out how actually be able to use TCP messages. Using an async callback I can receive messages but since the method is static I can't access it or call non static methods from it - so the messages may as well not exist. Non async methods either block (so the GUI is frozen) or chew up ALL cpu time looping.

Can anyone point me at some examples that let me access the messages without blocking or freezing the main program?
 
Here is an example I wrote in VB some time ago. See what you can make of it and feel free to ask me any questions, including how to convert any specific parts to C#. Obviously the syntax will differ in places but the principles are exactly the same between the two languages.
 
It's also worth noting that, since .NET 4.5, there are also methods that support the TPL, e.g. TcpListener.AcceptTcpClientAsync. Maybe it's time that I updated that example to use those. I'll look into that but it's not going to happen immediately.
 
I posted this somewhere else before, but here is an example of a multi-threaded TCP server with asynchronous connection handling:

C#:
   // We use a wait handle here to synchronize the client threads with the main thread.
    private static AutoResetEvent _waitHandle = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        // Start the server on port 1337
        StartServer(1337);
    }

    private static void StartServer(int port)
    {
        // Create a connection listener
        var listener = new TcpListener(IPAddress.Any, port);

        try
        {
            // Start the listener
            listener.Start();
            while (true)
            {
                // Wait for a connection, and defer connection handling asynchronously.
                listener.BeginAcceptTcpClient(new AsyncCallback(HandleAsyncConnection), listener);
                _waitHandle.WaitOne();
                _waitHandle.Reset();
            }
        }
        catch (SocketException ex)
        {
            // Handle socket errors or any other exception you deem necessary here
        }
        finally
        {
            // Stop the server.
            listener.Stop();
        }
    }

    private static void HandleAsyncConnection(IAsyncResult state)
    {
        // Get the listener and the client references
        var listener = (TcpListener)state.AsyncState;
        using (var tcpClient = listener.EndAcceptTcpClient(state))
        {
            // Signal the main thread that we have started handling this request.
            // At this point the server is ready to handle another connection, and no amount
            // of tomfoolery on the client's side will prevent this.
            _waitHandle.Set();

            // Declare buffers
            var inBuff = new byte[tcpClient.ReceiveBufferSize];
            var outBuff = new byte[tcpClient.SendBufferSize];

            // Get the connection stream
            using (var stream = tcpClient.GetStream())
            {
                try
                {
                    // Read some data into inBuff
                    stream.Read(inBuff, 0, tcpClient.ReceiveBufferSize);

                    // Do something with the data here, put response in outBuff...

                    // Send response to client
                    stream.Write(outBuff, 0, outBuff.Length);
                }
                catch (SocketException ex)
                {
                    // Handle socket errors or any other exception you deem necessary here
                }
            }
        }
    }
 
Here is an example I wrote in VB some time ago. See what you can make of it and feel free to ask me any questions, including how to convert any specific parts to C#. Obviously the syntax will differ in places but the principles are exactly the same between the two languages.

Very interesting. I used VB for years and even learned a bit of VB.net so I'll have a good look - thanks!
 
Isn't this still unavailable in the main thread since the methods/vars are all static?

The code I posted is an example from a console application, for a form just put it in a class and remove the static keyword. You will also need to modify the structure a bit to start the server on a secondary thread, and provide a StopServer method, etc...

Something like this:

C#:
public class MyTCPServer
{
    // We use a wait handle here to synchronize the client threads with the main thread.
    private AutoResetEvent _waitHandle = new AutoResetEvent(false);
    
    private int _port;

    private bool _stopRequested = false;
    private bool _isStopped = true;
        
    public MyTCPServer(int port)
    {
        _port = port;
    }
    
    public void Start()
    {
        // Stop the running server
        Stop();
        
        // Start the server on the threadpool. In reality you should probably use a new Thread directly and keep track of it.
        // Not within the scope of this example though...
        _isStopped = false;
        ThreadPool.QueueUserWorkItem(new WaitCallback(x => { RunServer(_port); }));       
    }
    
    public void Stop()
    {
        // Set a flag to signal the server thread to stop after handling the current connection request and wait for completion.
        _stopRequested = true;
        while (!_isStopped) Thread.Sleep(0);
        _stopRequested = false;
    }

    private void RunServer(int port)
    {
        // Create a connection listener
        var listener = new TcpListener(IPAddress.Any, port);

        try
        {
            // Start the listener
            listener.Start();
            while (!_stopRequested)
            {
                // Wait for a connection, and defer connection handling asynchronously.
                listener.BeginAcceptTcpClient(new AsyncCallback(HandleAsyncConnection), listener);
                _waitHandle.WaitOne();
                _waitHandle.Reset();
            }
        }
        catch (SocketException ex)
        {
            // Handle socket errors or any other exception you deem necessary here
        }
        finally
        {
            // Stop the server.
            listener.Stop();
            _isStopped = true;
        }
    }

    private void HandleAsyncConnection(IAsyncResult state)
    {
        // Get the listener and the client references
        var listener = (TcpListener)state.AsyncState;
        using (var tcpClient = listener.EndAcceptTcpClient(state))
        {
            // Signal the main thread that we have started handling this request.
            // At this point the server is ready to handle another connection, and no amount
            // of tomfoolery on the client's side will prevent this.
            _waitHandle.Set();

            // Declare buffers
            var inBuff = new byte[tcpClient.ReceiveBufferSize];
            var outBuff = new byte[tcpClient.SendBufferSize];

            // Get the connection stream
            using (var stream = tcpClient.GetStream())
            {
                try
                {
                    // Read some data into inBuff
                    stream.Read(inBuff, 0, tcpClient.ReceiveBufferSize);

                    // Do something with the data here, put response in outBuff...

                    // Send response to client
                    stream.Write(outBuff, 0, outBuff.Length);
                }
                catch (SocketException ex)
                {
                    // Handle socket errors or any other exception you deem necessary here
                }
            }
        }
    }
}
 
Last edited:
Back
Top Bottom