You are on page 1of 13

Asynchronous Socket Programming in C#:

Part I
http://www.codeguru.com/csharp/csharp/cs_network/sockets/article.php/c7695

Objective

The objective of this article is to demonstrate a socket-based client/server application that will
allow two-way asynchronous communication between a server and multiple client
applications. Because this example uses asynchronous methods, the server application does
not use threads to communicate to multiple clients (although internally the asynchronous
communication mechanism uses threads at the OS level).

The Difference Between Synchronous and Asynchronous Communication in


Network Programming

The key difference between synchronous and asynchronous communication can be explained
with an example.

Consider a server application that is listening on a specific port to get data from clients. In
synchronous receiving, while the server is waiting to receive data from a client, if the stream
is empty the main thread will block until the request for data is satisfied. Hence, the server
cannot do anything else until it receives data from the client. If another client attempts to
connect to the server at that time, the server cannot process that request because it is blocked
on the first client. This behavior is not acceptable for a real-world application where we need
to support multiple clients at the same time.

In asynchronous communication, while the server is listening or receiving data from a client,
it can still process connection requests from other clients as well as receive data from those
clients. When a server is receiving asynchronously, a separate thread (at the OS level) listens
on the socket and will invoke a callback function (specified when the asynchronous listening
was commenced) when a socket event occurs. This callback function in turn will respond and
process that socket event. For example, if the remote program writes some data to the socket,
a "read data event" (callback function you specify) is invoked; it knows how to read the data
from the socket at that point.

Even though this could be achieved by running multiple threads, the C# and .NET
frameworks provide a rich set of functionalities to do asynchronous communications without
introducing the complexity of threading.

Socket Class

The Socket class (System.Net.Sockets.Socket) provides a set of synchronous and


asynchronous methods for synchronous or asynchronous communication. As per the .NET
naming convention, all the asynchronous method names are created by prefixing the words
"Begin" or "End" to the name of the synchronous methods. The methods prefixed with
"Begin" and "End" represent a pair of asynchronous methods corresponding to a single
synchronous method, as shown in the following table.
Synchronous Methods Asynchronous Methods
Connect() BeginConnect()
EndConnect()
Receive() BeginReceive()
EndReceive()

Example Application

The example shown in this article has two classes, one implementing the Socket Server and
the other implementing the Socket Client.

Socket Server Implementation

The Socket Server application is implemented in the SocketServer class (file name
SocketServer.cs). This class has a main Socket object (m_mainSocket) and an array of worker
Socket objects (m_workerSocket) as members. The main Socket object does the listening for
the clients. Once a client is connected, the main Socket transfers the responsibility to process
the transactions related to that particular client to a worker Socket. Then, the main Socket
goes back and continues listening for other clients.

BeginAccept() and BeginReceive() are the two important methods in the Socket class used by
the Socket Server application.

The BeginAccept() method has the following signature:

public IAsyncResult BeginAccept(


AsyncCallback callback, // (1) Function to call when a client
// is connected
object state // (2) State object to preserve socket
// info
);
Essentially, after calling the Listen() method of the main Socket object, you call this
asynchronous method and specify a call back function (1), which you designated to do the
further processing related to the client connection. The state object (2) can be null in this
particular instance.

Because this is an asynchronous method, it will return immediately and the server main thread
is free to process other events. Behind the scenes, a separate thread will start listening on that
particular socket for client connections. When a client requests a connection, the callback
function you specified will be invoked.

Inside the callback function (in the example, the function is named "OnClientConnect()"), you
will do further processing related to the client connection.

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
m_workerSocket[m_clientCount] = m_mainSocket.EndAccept (asyn);
// Let the worker Socket do the further processing for the
// just connected client
WaitForData(m_workerSocket[m_clientCount]);
// Now increment the client count
++m_clientCount;
// Display this client connection as a status message on the GUI
String str = String.Format("Client # {0} connected",
m_clientCount);
textBoxMsg.Text = str;

// Since the main Socket is now free, it can go back and wait
// for other clients who are attempting to connect
m_mainSocket.BeginAccept(new AsyncCallback
( OnClientConnect ),null);
}
catch(ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0,"1","\n OnClientConnection:
Socket has been closed\n");
}
catch(SocketException se)
{
MessageBox.Show ( se.Message );
}

The first thing you do inside the "OnClientConnect()" function is to call the EndAccept()
method on the m_mainSocket member object, which will return a reference to another socket
object. You set this object reference to one of the members of the array of Socket object
references you have (m_workerSocket) and also increment the client counter. Now, because
you have a reference to a new socket object that now can do the further transaction with the
client, the main Socket (m_mainSocket) is free; hence, you will call its BeginAccept() method
again to start waiting for connection requests from other clients.
On the worker socket, you use a similar strategy to receive the data from the client. In place of
calling BeginAccept() and EndAccept(), here you call BeginReceive() and EndReceive().
This, in a nutshell, is the Socket Server implementation. While you are sending out data to the
clients, the server simply uses the specific worker socket objects to send data to each client.

Socket Client Implementation

(Full Size Image)

The Socket Client application is implemented in the SocketClient class (file name
SocketClient.cs). Compared to the server where you have a main Socket and an array of
worker Sockets, here you only have a single Socket object (m_clientSocket).

The two important methods in Socket class used by the Socket Client application are the
Connect() and BeginReceive() methods. Connect() is a synchronous method and is called to
connect to a server that is listening for client connections. Because this call will succeed/fail
immediately, depending on whether there is an active server listening or not at the specified
IP and Port number, a synchronous method is okay for this purpose.

Once a connection is established, you call the BeginReceive() asynchronous function to wait
for any socket write activity by the server. Here, if you call a synchronous method, the main
thread on the client application will block and you will not be able to send any data to the
server while the client is waiting for data from the server.

When there is any write activity on the socket from the server end, the internal thread started
by BeginReceive() will invoke the callback function ("OnDataReceived()" in this case),
which will take care of the further processing of the data written by the server.

When sending the data to the server, you just call the Send() method on the m_clientSocket
object, which will synchronously write the data to the socket.

That is all there is for asynchronous socket communication using multiple clients.

Limitations/Possible Improvements
 Up to 10 simultaneous clients are supported. You can easily modify and support
unlimited number of clients by using a HashTable instead of an array.
 For simplicity, when the server sends out a message, it is broadcast to all the
connected clients. This could easily be modified to send messages to specific clients
by using the Socket object pertaining to that particular client.
 When a client is disconnected, proper action is not taken; the client count is not
decremented. The correct way would be to reuse or release resources for other client
connections.

Acknowledgement

Even though the content of this article is independently developed, the example program used
is influenced by the article on Socket Programming in C# by Ashish Dhar.

Update added on 03/01/2005

For a more comprehensive example covering topics such as thread synchronization,


please see Part II of this article.

About the Author


Jayan Nair is a Senior Software Engineer with 11+ 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
 async_client_server_exe.zip
 async_client_server_src.zip
Asynchronous Socket Programming in C#:
Part II
http://www.codeguru.com/Csharp/Csharp/cs_network/sockets/article.php/c87
81/#Client1

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

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 is a Senior Software Engineer with 11+ 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
 async_client_server_II_exe.zip
 async_client_server_II_src.zip

You might also like