Asynchronous Programming

Asynchronous Programming
Most of the socket functions in the original Berkeley library take an indefinite amount of time to complete execution. In such situations, the function is said to be blocked; this means that calling the function would block the current thread of execution-if there is only one thread in the application then the whole application will have to wait for the function to return before continuing execution. For example, the send and receive functions are said to be blocked as they do not return immediately.

With asynchronous programming, a connection does not block an application while waiting for network processes to complete. Instead of blocking the current thread it uses the .NET asynchronous model to process the network connections on another thread while the application continues to run in the original thread. When the second thread has completed its execution, it sends an event object to the original thread to indicate the status of the task. Events can be either signaled, in which case methods that are waiting on the event will return immediately, or non-signaled. If the event is non-signaled, the waiting method will be blocked until the event is signaled.

There are two types of events-automatic events, which set themselves back to the non-signaled state whenever they are signaled, and manual reset events, which stay signaled until they are manually reset to the non-signaled state. .NET has a ManualResetEvent class that we can use to indicate to our program that we want to continue-by setting a ManualResetEvent object to the signaled state, it will stop blocking any waiting methods, and the program can continue. We will use this object to facilitate our asynchronous programming.

The asynchronous programming model is suitable for applications that cannot afford to hang around waiting for network operations to complete before moving ahead. In the world of .NET, there is a complete set of methods for asynchronous operations. For example, BeginSend() is used instead of the normal Send() method when doing asynchronous programming.

The asynchronous model uses the callback method to return the result of the operation. The .NET Framework has a set of methods responsible for initiating a task in an asynchronous manner but they require a callback function to end the operation. For example the Connect() method uses BeginConnect() to start a connection to the network device and the corresponding callback function is required to complete the connection.

The .NET Framework provides a delegate class for asynchronous operations. The class, which is called AsyncCallback, provides a way for applications to complete an asynchronous operation.

AsyncCallback aCallback = new AsyncCallback(AsyncCallback);
This delegate is supplied as an argument to the asynchronous function when the operation is initiated. The function pointer referenced by AsyncCallback contains program logic to finish processing the asynchronous task for the client.

This programming model uses multiple threads to process network connections. To facilitate synchronization between these different threads, we use the ManualResetEvent class to suspend execution of the main class and signal when execution can continue.

We begin by building an asynchronous client application, which will be tested with the socket server built earlier. After that we complete the asynchronous discussion by building an asynchronous server to plug with the client.

Asynchronous Client Application
The following diagram will help you to understand the flow of an asynchronous client application.


Thus we have three tasks to accomplish asynchronously here-connecting to the server, sending data to the server, and receiving data from the server.

First let's discuss how we're going to synchronize everything. The code will need to be synchronized because of its asynchronous nature-you couldn't work with the socket if you haven't finished connecting to the server, or a deadlock could result if we were to attempt to receive data when we should instead be sending. We will use the ManualEventReset class to synchronize the different threads that will be produced.

The complete code for this example can be found in the file ASyncClient.cs.


public class AsyncClient
{
public static string theResponse = "";
public static byte[] buffer = new byte[1024];

public static ManualResetEvent ConnectDone = new ManualResetEvent(false);
public static ManualResetEvent SendDone = new ManualResetEvent(false);
public static ManualResetEvent ReceiveDone = new ManualResetEvent(false);

A ManualResetEvent object can have its state set and reset (that is, be set to the signaled or nonsignaled state) with its Set() and Reset() methods. Each of three tasks, connecting, sending, and receiving, will have a ManualResetEvent object attached, and the main thread can wait for the relevant task to complete by calling the WaitOne() method of the relevant ManualResetEvent object -this method blocks the current thread until the object is signaled. When we create the ManualResetEvent objects above, the value of false indicates that the initial state is not set. Of course the main thread can continue with other processing, but before the next task begins we have to be certain that the previous one has completed-this is precisely where WaitOne() comes in.

Before we look at our first task, let's create and initialize the socket we're going to use.


IPHostEntry ipHost = Dns.Resolve("127.0.0.1");
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddr, 11000);

Socket sClient = new Socket(AddressFamily, Internetwork,
SocketType.Stream, ProtocolType.Tcp);

Our first task is to connect to the server. This is achieved with the BeginConnect() method. The BeginConnect() method starts an asynchronous connection request with the remote host. It requires a callback method that implements the AsyncCallback delegate. This callback method should call EndConnect() to end the pending connection request and return the connected socket.


sClient.BeginConnect(endPoint, new AsyncCallback(ConnectCallback), sClient);

There are three parameters to this method; the first represents the remote host using the IPEndPoint class, the second argument is the AsyncCallback delegate that is used for passing the function pointer, and the third argument is an object containing state information to be passed to the callback method specified. Here, the socket instance is being passed.

Let's look at the ConnectCallBack() method:


public static void ConnectCallback(IAsyncResult ar)
{
Thread thr = Thread.CurrentThread;
Console.WriteLine("ConnectCallback Thread State:" + thr.Threadstate);

First of all, we inspect the thread that our asynchronous method is running in. Thread information can be obtained from the CurrentThread property of the Thread object. This will show that the code in our asynchronous method runs in a background thread.

Next, let's see how to retrieve the Socket that we're actually working on. The IAsyncResult parameter contains information about the state of the asynchronous operation. Using the AsyncState property of the IAsyncResult interface we can retrieve the argument that we passed as the third parameter of the BeginConnect() method. The value retrieved needs to be cast to a Socket.


Socket sClient = (Socket) ar.AsyncState;

Next, the EndConnect() method is called to complete the asynchronous request. If some error occurred when accessing the socket, EndConnect() will throw a SocketException.


sClient.EndConnect(ar);

Console.WriteLine("Socket connected to {0}",
sClient.RemoteEndPoint.ToString());

Finally, we call the Set() method of the ConnectDone ManualResetEvent-this will indicate to the main thread that we've finished connecting.


ConnectDone.Set();
}

Now that we've connected to the server, the next step is to communicate with the server by sending and receiving data.

The BeginSend() method is used to send data asynchronously to the connected Socket.


sClient.BeginSend(byteData, 0 , byteData.Length, 0 ,
new AsyncCallback(SendCallback), sClient);

The first parameter is a byte array buffer containing the data to send, the second argument represents the position in the buffer from which to begin sending data, the third parameter is the buffer size, the fifth parameter is the AsyncCallback delegate, and the last parameter is used to store state information.

The BeginSend() method calls the callback function passed through the AsyncCallback delegate:


public static void SendCallback(IAsyncResult ar)
{
Thread thr = Thread.CurrentThread;
Console.WriteLine("SendCallback Thread State:" + thr.ThreadState);

Socket sClient = (Socket) ar.AsyncState;

int bytesSent = sClient.EndSend(ar);

Console.WriteLine("Sent {0} bytes to server.", bytesSent);

SendDone.Set();
}

The code is similar to the ConnectCallback() function, apart from the call to EndSend(), which ends the asynchronous send request, and the setting of the SendDone ManualResetEvent.

The BeginReceive() method start asynchronously receiving data from a Socket:


sClient.BeginReceive(buffer, 0 , buffer.Length , 0,
new AsyncCallback(ReceiveCallback), sClient);

The arguments are similar to the BeginSend() method. The callback method should use the EndReceive() method to return the data read from the Socket.


public static void ReceiveCallback(IAsyncResult ar)
{
Thread thr = Thread.CurrentThread;
Console.WriteLine("ReceiveCallback Thread State;" + thr.Threadstate);

Socket sClient = (Socket) ar.AsyncState;

int bytesRead = sClient.EndReceive(ar);

if (bytesRead > 0)
{
theResponse += Encoding.ASCII.GetString(buffer, 0 , bytesRead);

sClient.BeginReceive(buffer, 0 , buffer.Length ,0 ,
new AsyncCallback(ReceiveCallback), sClient);
}
else
{
ReceiveDone.Set();
}
}

To ensure that we've received all of the data, BeginReceive() is called inside the callback. The EndReceive() method returns the number of bytes, so we can check if there is some data back in the queue. The returned data is then stored in a string field, theResponse.

Let's look at the Main() method that ties everything together-we've already seen some of this code. We start by displaying information about the current thread, to distinguish between the thread on which the socket is operating and the main thread. Then we create our socket as we've seen earlier:

public static void Main(string [] arg)
{
try
{

Thread thr = Thread.CurrentThread;
Console.WriteLine("Main Thread State:" + thr.ThreadState);

IPHostEntry ipHost = Dns.Resolve("127.0.0.1");
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddr, 11000);
Socket sClient = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
We can now begin establishing our connection. We call the BeginConnect() method, specifying the ConnectCallBack() method, and wait for the operation to be completed before sending a message to the connected socket. The main thread is blocked with ConnectDone.WaitOne()-this means that we will not move onto sending data before the connection has completed successfully. This is signaled with the Set() method of the ConnectDone object in ConnectCallBack().

sClient.BeginConnect(endPoint, new AsyncCallback(ConnectCallback),
sClient);

ConnectDone.WaitOne();
Now we'll define the message to send to the server-we bloat it with some string concatenation to create a bigger message. This will give us more of an opportunity to see the asynchronous operations in action.


string data = "This is a test.";
for (int i=0;i<72;i++)
data += i.ToString()+":" + (new string('=',i));

byte[] byteData = Encoding.ASCII.GetBytes(data+"");

Now we can start the asynchronous send operation-we have our message and we know that connection has been established:

sClient.BeginSend(byteData, 0 , byteData.Length, 0 ,
new AsyncCallback(SendCallback), sClient);
We 'perform some other processing' to illustrate that our client is genuinely running asynchronously. Here we simply put the current Thread to sleep for a hundredth of a second while looping-this provides a convenient simulation of some processor-hungry computations.


for (int i=0;i<5;i++)
{
// Perform some other processing....
Console.WriteLine(i);
Thread.Sleep(10);
}

Before we try to receive data from the server all the data needs to have been sent. Thus we use SendDone.WaitOne() to block the thread until SendCallBack() signals sending is finished with SendDone.Set().

// Before we can receive we must have finished sending
SendDone.WaitOne();
sClient.BeginReceive(buffer, 0 , buffer.Length, 0,
new AsyncCallback(ReceiveCallback), sClient);
Finally, we wait for the data to be received before displaying it, and then shutdown and close our socket.


ReceiveDone.Wait One();

Console. WriteLine("Response received: {0} ", theResponse);

sClient.Shutdown(SocketShutdown.Both);
sClient.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

The output of our asynchronous client when running with the (synchronous) socket server we developed earlier is:


Asynchronous Server Application
The flow for our asynchronous server application is demonstrated in the following diagram:


The server starts by creating a socket and then listening to the port that it is bound to. The asynchronous server application requires BeginAccept() to accept connections asynchronously, and after the connection is established, it uses the BeginReceive() and BeginSend() methods to send data to and receive it from the client socket.

After BeginAccept() is called, we set an event in the waiting state, so that a different thread in the application continues to execute when a client tries to connect to it. If we don't do this, the application finishes before the client because of the asynchronous nature of the server. The ManualResetEvent class is used here to set and wait for events:

AsyncCallback aCallback = new AsyncCallback(AcceptCallback);

// sListener is an instance of the Socket class
sListener.BeginAccept(aCallback, sListener);

// assume that socketEvent is an instance of ManualResetEvent() class
socketEvent.WaitOne();
The BeginAccept() method takes two parameters: an asynchronous delegate which references the callback method and a parameter which is used to pass data to the callback function (in this case, the listener socket itself). The BeginAccept() method calls the AcceptCallback() function when a new connection is received on the socket. This callback function calls the EndAccept() method to return the new socket for the client.

Asynchronous sockets programming uses threads for different asynchronous operations. The main thread is responsible for initiating the listener socket, one thread is responsible for accepting incoming requests, and another thread is responsible for receiving and sending data. We can get the thread ID of the current thread using the GetCurrentThreadID() function of the AppDomain class; we display this number in the console, so you can see that the different operations are running on different threads:

Console.WriteLine("Main Thread ID: " + AppDomain.GetCurrentThreadId());
Notice that we don't need to start the separate threads explicitly-this is done for us by the .NET Framework.

After accepting the connection by calling EndAccept(), the new socket can communicate with the client by calling the asynchronous BeginReceive() and the BeginSend() methods:

Socket handler = listener.EndAccept(ar);

handler.BeginReceive(buffer, 0, buffer.Length, 0, new
AsyncCallback(ReceiveCallback), handler);
The ReceiveCallback() function first calls the EndReceive() function to complete the pending asynchronous task. Then we check whether the end-of-message character is found. If so, we use BeginSend() to send the data back to the client; otherwise, we call BeginReceive() to receive any data left:

int bytesRead = handler.EndReceive(ar);

if (bytesRead > 0)
{
content += Encoding.ASCII.GetString(buffer, 0, bytesRead);

if (content.IndexOf(".") > -1)
{
Console.WriteLine("Read {0} bytes from socket. \n Data: {1}",
content.Length, content);
byte[] byteData = Encoding.ASCII.GetBytes(content);

handler.BeginSend(byteData, 0 , byteData.Length, 0 ,
new AsyncCallback(SendCallback), handler);
}
else
{
handler.BeginReceive(buffer, 0 , buffer.Length, 0,
new AsyncCallback(ReceiveCallback), handler);
}
}
The SendCallback() method completes the operation by calling EndSend(), closes the handler socket, and sets the ManualResetEvent on the main thread so that the application can proceed:

int bytesSent = handler.EndSend(ar);

Console.WriteLine("Sent {0} bytes to Client.", bytesSent);

handler.Shutdown(SocketShutdown.Both);
handler.Close();

socketEvent.Set();
Here's the complete source for the AsyncServer.cs:


using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;

public class AsyncServer
{
// buffer to receive and send data
public static byte[] buffer = new byte[1024];

// the event class to support synchronization
public static ManualResetEvent socketEvent = new ManualResetEvent(false);

public static void Main(string [] args)
{

Console.WriteLine("Main Thread ID:" + AppDomain.GetCurrentThreadld());

byte[] bytes = new byte[1024];

IPHostEntry ipHost = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddr = ipHost.AddressList[0];

IPEndPoint localEnd = new IPEndPoint(ipAddr, 11000);

Socket sListener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);

// binding a socket
sListener.Bind(localEnd);

// start listening
sListener.Listen(10);

Console.WriteLine("Waiting for a connection...");

AsyncCallback aCallback = new AsyncCallback(AcceptCallback);

// asychronous funtion for accepting connections
sListener.BeginAccept(aCallback,sListener);

// waiting for the other threads to finish
socketEvent.WaitOne();
}

public static void AcceptCallback(IAsyncResult ar)
{
Console.WriteLine("AcceptCallback Thread ID:" +
AppDomain.GetCurrentThreadId());
// retrieved the socket
Socket listener = (Socket)ar.Async State;

// new socket
Socket handler = listener.EndAccept(ar);

handler.BeginReceive(buffer, 0 , buffer.Length, 0,
new AsyncCallback(ReceiveCallback) , handler);
}

public static void ReceiveCallback(IAsyncResult ar)
{
Console.WriteLine("ReceiveCallback Thread ID:" +
AppDomain.GetCurrentThreadld());

string content = String.Empty;

Socket handler = (Socket) ar.AsyncState;

int bytesRead = handler.EndReceive(ar);

// if there is some data ..
if (bytesRead > 0)
{
// append it to the main string
content += Encoding.ASCII.GetString(buffer, 0, bytesRead);

// if we encounter the end of message character ...
if (content.IndexOf(".") > -1)
{
Console.WriteLine("Read {0} bytes from socket. \n Data: {1}"/
content.Length, content);
byte[] byteData = Encoding.ASCII.GetBytes(content);

// send the data back to the client
handler.BeginSend(byteData, 0 , byteData.Length, 0 ,
new AsyncCallback(SendCallback), handler);
}
else
{
// otherwise receive the remaining data
handler.BeginReceive(buffer, 0 , buffer.Length, 0,
new AsyncCallback(ReceiveCallback), handler);
}
}
}

public static void SendCallback(IAsyncResult ar)
{
Console.WriteLine("SendCallback Thread ID:" +
AppDomain.GetCurrentThreadId());

Socket handler = (Socket) ar.AsyncState;

// send data back to the client
int bytesSent = handler.EndSend(ar);

Console.WriteLine("Sent {0} bytes to Client.", bytesSent);

// close down socket
handler.Shutdown(SocketShutdown.Both);
handler.Close();

// set the main thread's event
socketEvent.Set();
}

}