Asynchronous Socket Programming in C#: Part II

Motivation for This Article

After the original article on Asynchronous Socket Programming in C# was published by CodeGuru, I received numerous responses from interested readers. Most of them asked for additional features that were missing in the original example. The original article was intended to show a simplistic example for asynchronous socket programming. To keep the simplicity of the original example, instead of modifying it, I am providing a more comprehensive example by adding the features requested by the readers.

Requested Features Added

This example includes modifications to support the following features:

  1. How to support an unlimited number of clients
  2. How to find which client sent a particular message
  3. How to reply or send messages to specific clients
  4. How to find when a particular client is disconnected
  5. How to get the list of all connected clients at any given time
  6. Are variables safe in AsyncCallback methods? What about thread synchronization? [Updated on 02/01/05]

Other Enhancements

  1. On the server and client code, the receive buffer size is increased to 1024 instead of a single byte for more efficiency.
  2. Cleanup code is added after a client is disconnected.

Screen shot of Socket Server:

Screen shot of Socket Client:

How to Support an Unlimited Number of Clients

This was an easy feature to add. In the original article, an array of Socket objects was used to store the references to the worker sockets. This is now modified to use an ArrayList as shown in the following code. (A HashTable also would have worked if you wanted to use a string instead of an index to track the connected clients.)

Note: If you want to run your server for an infinite duration, there is the possibility of overflow of the integer value of the m_clientCount variable. In such scenarios, you may want to reconsider using this numbering scheme for clients. This example will still work on such scenarios, as long as you don't number your clients. But, this issue goes beyond the scope of this article.
// An ArrayList is used to keep track of worker sockets that are
// designed to communicate with each connected client
private System.Collections.ArrayList m_workerSocketList =
   new System.Collections.ArrayList();
// The following variable will keep track of the cumulative
// total number of clients connected at any time
private int m_clientCount = 0;

How to Find Which Client Sent a Particular Message

When multiple clients are connected, you may need to differentiate between the messages received from different clients. Also, there may be a reason to send a message to a particular client.

You could solve this problem by keeping track of each client by assigning them a serially incremented number as soon as they are connected to the server. Here is the code that does that:

public void OnClientConnect(IAsyncResult asyn)
{
   try
   {
      // Here we complete/end the BeginAccept() asynchronous call
      // by calling EndAccept(), which returns the reference to a
      // new Socket object
      Socket workerSocket = m_mainSocket.EndAccept (asyn);
      // Now, increment the client count for this client
      ++m_clientCount;
      // Add the workerSocket reference to the ArrayList
      // We will use (clientNumber - 1) as the index to access
      // this socket in the future
      m_workerSocketList.Add(workerSocket);
      //........
      // Let the worker Socket do the further processing for the
      // just-connected client
      WaitForData(workerSocket, m_clientCount);
      //........

Inside the WaitForData() function, you will make the actual asynchronous call to receive the data from the client as shown below:

public void WaitForData(System.Net.Sockets.Socket soc,
                        int clientNumber)
{
   try
   {
      if( pfnWorkerCallBack == null )
      {
         // Specify the callback function that is to be invoked when
         // there is any write activity by the connected client
         pfnWorkerCallBack = new AsyncCallback (OnDataReceived);
      }
      SocketPacket theSocPkt = new SocketPacket (soc, clientNumber);
      // Start receiving any data written by the connected client
      // asynchronously
      soc.BeginReceive (theSocPkt.dataBuffer, 0,
      theSocPkt.dataBuffer.Length,
      SocketFlags.None,
      pfnWorkerCallBack,
      theSocPkt);
      //........

In the above code, the user-defined class SocketPacket is the most critical item. As you can see, an object of this class is the last parameter passed to the asynchronous function call BeginReceive(). This object can contain any information that you find useful; it can be used later, when you actually receive the data from the client. You send (1) the worker socket object and (2) the index number of the client packaged inside this object. You will retrieve them back when you actually receive the data from a particular client.

Given below is the definition of the SocketPacket class.

public class SocketPacket
{
   // Constructor that takes a Socket and a client number
   public SocketPacket(System.Net.Sockets.Socket socket,
                       int clientNumber)
   {
      m_currentSocket = socket;
      m_clientNumber  = clientNumber;
   }
   public System.Net.Sockets.Socket m_currentSocket;
   public int m_clientNumber;
   // Buffer to store the data sent by the client
   public byte[] dataBuffer = new byte[1024];
}

In the above code, the SocketPacket class contains the reference to a socket, a data buffer of size 1024 bytes, and a client number. This client number will be available when you actually start receiving data from a particular client. By using this client number, you can identify which client actually sent the data.

To demonstrate this in the example code, the server will echo back to the client (after converting to upper case) the received message, using the correct socket object.

How to Reply or Send Messages to Specific Clients

You might have figured out this already. This is very simple to implement. Because the SocketPacket object contains the reference to a particular worker socket, you just use that object to reply to the client. Additonally, you also could send any message to any particular client by using the worker socket object stored in the ArrayList.

How to Find when a Particular Client is Disconnected

This is a bit harder to address. There may be other elegant ways to do this, but here is a simple way.

When a client is disconnected, there will be a final call to the OnDataReceived() function. If nothing in particular is done, this call will throw a SocketException. What you can do here is to look inside this exception and see whether this was triggered by the "disconnection" of a client. For this, you will look at the error code inside the exception object and see whether it corresponds to 10054. If so, you will do the required action corresponding to the client disconnection. Here again, the SocketPacket object will give you the index number of the client that was disconnected.

catch(SocketException se)
{
   if(se.ErrorCode == 10054)    // Error code for Connection reset
                                // by peer
   {
      string msg = "Client " + socketData.m_clientNumber +
                   " Disconnected" + "\n";
      richTextBoxReceivedMsg.AppendText(msg);

      // Remove the reference to the worker socket of the closed
      // client so that this object will get garbage collected
      m_workerSocketList[socketData.m_clientNumber - 1] = null;
      UpdateClientList();
   }
   else
   {
      MessageBox.Show (se.Message );
   }
}

How to Get the List of All Connected Clients at Any Given Time

To show this, a dynamic list is displayed on the server GUI that will be updated (see the UpdateClientList() function) whenever a client is connected or disconnected.

Are Variables Safe in AsyncCallback Methods? What About Thread Synchronization?

This is a very valid question. For simplicity, I ignored this aspect in the first part of this article. Asynchronous programming using asynchronous delegates is just a matter of convenience. When you use asynchronous calls, you should be aware that, behind the scenes, you are actually using threads to achieve the asynchronous nature of these calls.

The following picture shows a simple illustration of the interplay of threads involved in this example.

In the above picture, the item labeled (1) is the main GUI thread that starts when you start the Server application. The thread labeled (2) starts whenever any client tries to connect to the socket. The thread labeled (3) spawns when there is any write activity by any one of the connected clients.

In the example code, the asynchronous functions OnClientConnect() and OnDataReceived() are called by threads other than the main GUI thread. Any other functions called inside these two functions are also invoked by threads other than the main GUI thread.

Threading issues to consider

  1. Shared variables

    Any shared variables that you modify inside the shared code mentioned above must be protected by synchronization structures. In this example, the shared variables you modify within the shared code are m_clientCount and m_workerSocketList.

    You can use very simple strategies to protect these variables. The m_clientCount variable is an integer variable and hence can be incremented by using the static method within the Interlocked class as shown below:

    // Now increment the client count for this client
    // in a thread safe manner
    Interlocked.Increment(ref m_clientCount);
    

    Similarly, you can protect the m_workerSocketList member variable from modification by multiple threads at the same time, by creating a Synchronized ArrayList as shown below:

    private System.Collections.ArrayList m_workerSocketList =
       ArrayList.Synchronized(new System.Collections.ArrayList());
    
  2. Modifying the GUI
  3. The main GUI thread actually owns the GUI controls. Hence, in production code, it is not recommended or advisable to access or modify any of the GUI controls by threads other than the main thread. When you need to update the GUI, you should make the main thread do it for you as shown in the following code:

    // This method could be called by either the main thread or
    // any of the worker threads
    private void AppendToRichEditControl(string msg) 
    {
       // Check to see if this method is called from a thread
       // other than the one created the control
       if (InvokeRequired)
       {
          // We cannot update the GUI on this thread.
          // All GUI controls are to be updated by the main (GUI)
          // thread.
          // Hence, we will use the invoke method on the control
          // that will be called when the Main thread is free
          // Do UI update on UI thread
          object[] pList = {msg};
          richTextBoxReceivedMsg.BeginInvoke(new
             UpdateRichEditCallback(OnUpdateRichEdit), pList);
       }
       else
       {
       // This is the main thread which created this control,
       // hence update it directly
          OnUpdateRichEdit(msg);
       }
    }
    
    // This UpdateRichEdit will be run back on the UI thread
    // (using System.EventHandler signature so we don't
    // need to define a new delegate type here)
    private void OnUpdateRichEdit(string msg) 
    {
       richTextBoxReceivedMsg.AppendText(msg);
    }
    

Acknowledgement

Part II of this article was developed to address the questions and comments I received from readers after publishing Part I of this article. The example programs used in Part I which are further enhanced and extended for Part II, are influenced by the article on Socket Programming in C# by Ashish Dhar.

Final Comments

Network programming is a very interesting topic. The C# language provides you with all the tools necessary to quickly develop networked applications. Compared to C++, Java and C# have a richer set of programming APIs, which will eliminate most of the complexities previously associated with network programming. This example incorporates all the features that the readers requested. Even then, use this example only as a learning tool. As you learn more about socket programming, you will realize the necessity to add thread synchronization constructs as well see the opportunities for further optimization, to solve the problem at your hand. Good luck with your learning and thanks for reading.



About the Author

Jayan Nair

Jayan Nair is a Senior Software Engineer with 15+ years of experience working with cutting edge software technologies. Currently he is developing the next generation software applications for the telecommnunications testing industry. Jayan's passions: Object Oriented software design and developing reusable software components. His motto: "if the software you write is not reusable, you are not writing software, but hardware instead". Jayan finished his Masters degree in Computer Science from Virginia Tech, Blacksburg, VA. His expertise includes, C, C++, Java, J2EE, Visual Basic, C#, ASP.NET and distributed applications. He is also a Sun Certified Programmer for the Java Platform (SCPJ). You can contact him at jnair1998@hotmail.com.

Downloads

Comments

  • Mr.

    Posted by Sam on 05/26/2014 01:59am

    Mr Jayan Nair, I need help for Socket programming for Mobile 6.5 sending multiple file to server/computer in socket programming tq sam

    Reply
  • Thanks

    Posted by namnew on 03/27/2014 01:24am

    i used this source to my code's delegate problem. i register to client and message(send,receive) in 3 GridViews

    Reply
  • Brief piece of content exposes the unquestionable details about chanel and in what way it may may affect users.

    Posted by emeseesip on 05/07/2013 10:18pm

    Most Comprehensive adidas Report You Ever Read Otherwise Your Money Back [url=http://www.guccija.biz/]グッチ 長財布[/url] Wow, remarkable item. Users gotta go look at adidas right away whilst it is still up for grabs ! ! [url=http://www.guccija.biz/]gucci キーケース[/url] nike will help every one of us by integrating quite a few unique functions and options. It is a unvaluable item for all enthusiast of nike. [url=http://www.guccija.biz/]gucci バッグ[/url] Impartial website brings out Five all new things on nike that no one is speaking of. [url=http://www.chanelja.biz/]シャネル 財布[/url] This is why not a soul is covering nike and therefore know what you ought to begin doing as we speak. [url=http://www.chanelja.biz/]chanel バッグ[/url] Recent questions about adidas have been answered and as a result the reason why you really should read through each and every statement on this article. [url=http://www.chanelja.biz/]財布 chanel[/url] Fundamental principles of nike you could potentially make full use of starting today.[url=http://www.nikeja.biz/]ナイキランニング[/url] A way to understand every part there is to understand surrounding adidas in three very easy steps.

    Reply
  • 1testing_post

    Posted by roarveCam on 03/20/2013 02:41pm

    kak uznat, ответы на вопросы где купить лыжи как расчитать больничные листы как делать эпиляция То, что лейтенант решила использовать для этого рабочих, не состоящих в профсоюзе, вряд ли необычно и не могло выглядеть провокацией.

    Reply
  • LaGaver

    Posted by Jose Luis BC on 01/09/2013 11:23pm

    Thanks You very Much! Great example. Learned a lot. Very useful code, and very well described.

    Reply
  • Good job

    Posted by rajeev on 01/01/2013 07:14am

    Hi the topic is very useful thank u

    Reply
  • Infinite Loop

    Posted by Tom Jose on 12/10/2012 12:33pm

    The program hangs repeating one client number, while connecting 30+ clients and hangs.

    Reply
  • Awsome

    Posted by Marcos on 11/29/2012 02:14pm

    Very useful code, and very well described, the comments and the variables declares. Appreciated it a lot. Thanks. Just a question... Did i have to make a AsyncCallBack to every time that i use Form Controls on the method Async onReceived?

    Reply
  • MZ

    Posted by juju_92 on 11/16/2012 02:35pm

    Great Code thanks a lot!! But how could I make the client and server work at the same time??!!!!

    Reply
  • qsIFY cnB VwBP

    Posted by jIGVExCdDp on 11/15/2012 06:16am

    soma drug carisoprodol y meloxicam - carisoprodol 724

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds