How to implement an asynchronous socket in C#

Asynchronous sockets increase the performance and scalability of your client-server applications

Asynchronous Sockets

Asynchronous Sockets

A socket is defined as the end point of a two way communication between two processes running over a network. Inter-process communication can be achieved using sockets. After a connection between the server and client, i.e., the server process and the client process is established, they can communicate for the purpose of exchanging data using sockets.

Why do we need asynchronous sockets?

Asynchronous programming enables you to execute tasks sans the need of holding up the execution flow or responsiveness of your application. This in turn helps to improve the performance and responsiveness of your application.

You can also build synchronous sockets, but such sockets don't scale well since they block your thread. Asynchrony can perform resource-intensive I/O operations sans the need to block the main or the executing thread of your application.

Implementing an asynchronous client-server socket application

To implement a TCP server-client socket communication, you would typically need to create a server process that should start at a particular port and also a client process that can start on any port and send a connection request to the server. The server process after it is started, listens for incoming connection requests at the port on which it has been started.

The following code snippet illustrates how you can create a Tcp socket and start it at a particular port.

IPHostEntry ipHostEntry = Dns.Resolve(Dns.GetHostName());

IPAddress ipAddress = ipHostEntry.AddressList[0];

IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888);

TcpListener serverSocket = new TcpListener(ipEndPoint);

serverSocket.Start();

Let's now create a class named ServerSocket that represents the asynchronous server socket class. This class would wrap the logic to create and start a Tcp socket and also wait for incoming client connections.

public class SocketServer

   {

       private static TcpListener serverSocket;

       public async void StartServer()

       {

           IPHostEntry ipHostEntry = Dns.Resolve(Dns.GetHostName());

           IPAddress ipAddress = ipHostEntry.AddressList[0];

           IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888);

           while (true)

           {

               serverSocket = new TcpListener(ipEndPoint);

               serverSocket.Start();

               Console.WriteLine("Server Socket started...waiting for connections...");

               try

               {

                   var tcpClient = await serverSocket.AcceptTcpClientAsync();

                   HandleConnectionAsync(tcpClient);

               }

               catch (Exception ex)

               {

                   Console.WriteLine(ex.ToString());

               }

           }

       }

   }

The HandleConnectionAsync would be used to process the individual clients.

private async void HandleConnectionAsync(TcpClient tcpClient)

       {

           //Write code here to process the incoming client connections

       }

Let's now modify the code of the StartServer method to eliminate the looping - we can use an async model in lieu of looping for efficiency, i.e., improved performance.

public class SocketServer

{

private static TcpListener serverSocket;

public static void StartServer()

       {

           IPHostEntry ipHostEntry = Dns.Resolve(Dns.GetHostName());

           IPAddress ipAddress = ipHostEntry.AddressList[0];

           IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888);

           serverSocket = new TcpListener(ipEndPoint);

           serverSocket.Start();

           Console.WriteLine("Asynchonous server socket is listening at: " + ipEndPoint.Address.ToString());

           WaitForClients();

       }

}

Refer to the ServerSocket class shown above. The static method StartServer of the ServerSocket class contains the necessary code to create a socket and start it. It also waits for incoming client connections sans the need of looping. Note the call to the WaitForClients method in the last statement of the StartServer method. The WaitForClients() method is shown below.

private static void WaitForClients()

       {

           serverSocket.BeginAcceptTcpClient(new System.AsyncCallback(OnClientConnected), null);

       }

The BeginAcceptTcpClient method has been used to asynchronously accept client connections. It should be noted that the BeginAcceptTcpClient operation must be completed by invoking the EndAcceptTcpClient method on the socket instance.

The OnClientConnected callback method is shown below.

       private static void OnClientConnected(IAsyncResult asyncResult)

       {

           try

           {

               TcpClient clientSocket = serverSocket.EndAcceptTcpClient(asyncResult);

               if (clientSocket != null)

               Console.WriteLine("Received connection request from: " + clientSocket.Client.RemoteEndPoint.ToString());

               HandleClientRequest(clientSocket);

           }

           catch

           {

               throw;

           }

           WaitForClients();

       }

Here's the signature of the HandleClientRequest method that does the actually processing of incoming client requests.

       private static void HandleClientRequest(TcpClient clientSocket)

       {

           //Write your code here to process the data

       }

The following code snippet shows how you can start the server socket.

static void Main(string[] args)

       {

           SocketServer.StartServer();

           Console.Read();

       }

The asynchronous socket client

At the client side, you would need to have a client socket running that would connect to the server socket and, send and receive data from the server socket. The following class named IDGSocketClient represents our asynchronous socket client.

public class IDGSocketClient

   {

       System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();

       NetworkStream networkStream;

       public void Connect(string ipAddress, int port)

       {

           clientSocket.Connect(ipAddress, port);

       }

        public void Send(string data)

       {

           //Write code here to send data

       }

       public void Close()

       {

           clientSocket.Close();

       }

       public string Receive()

       {

           //Write code here to receive data from the server

       }

   }

The following code snippet illustrates how you can connect to the server socket from the client and send data.

static void Main(string[] args)

       {

           IDGSocketClient client = new IDGSocketClient();

           client.Connect("127.0.0.1",8888);

           client.Send("Hello");

           Console.Read();

       }

This article is published as part of the IDG Contributor Network. Want to Join?

To comment on this article and other InfoWorld content, visit InfoWorld's LinkedIn page, Facebook page and Twitter stream.
From CIO: 8 Free Online Courses to Grow Your Tech Skills
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.