Socket Programming in C# - Part 2

Getting Started

You can argue that one can overcome these shortcomings by multithreading meaning that one can spawn a new thread and let that thread do the polling which then notifies the main thread of the data. This concept could work well, but even if you create a new thread it would require your main thread to share the CPU time with this new thread. Windows operating system (Windows NT /2000 /XP) provide what is called Completion Port IO model for doing overlapped ( asynchronous) IO.

The details of IO Completion port are beyond the scope of the current discussion, but to make it simple you can think of IO Completion Ports as the most efficient mechanism for doing asynchronous IO in Windows that is provided by the Operating system. Completion Port model can be applied to any kind of IO including the file read /write and serial communication.

The .NET asynchronous socket programming helper class's Socket provides the similar model.

BeginReceive

.NET framework's Socket class provides BeginReceive method to receive data asynchronously i.e., in an non-blocking manner The BeginReceive method has following signature:

public IAsyncResult BeginReceive( byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state );

The way BeginReceive function works is that you pass the function a buffer , a callback function (delegate) which will be called whenever data arrives.

The last parameter, object, to the BeginReceive can be any class derived from object ( even null ) . When the callback function is called it means that the BeginReceive function completed which means that the data has arrived. The callback function needs to have the following signature:

void AsyncCallback( IAsyncResult ar);

As you can see the callback returns void and is passed in one parameter , IAsyncResult interface , which contains the status of the asynchronous receive operation.

The IAsyncResult interface has several properties. The first parameter - AsyncState - is an object which is same as the last parameter that you passed to BeginReceive(). The second property is AsyncWaitHandle which we will discuss in a moment. The third property indicates whether the receive was really asynchronous or it finished synchronously. The important thing to follow here is that it not necessary for an asynchronous function to always finish asynchronously - it can complete immediately if the data is already present. Next parameter is IsComplete which indicates whether the operation has completed or not.

If you look at the signature of the BeginReceive again you will note that the function also returns IAsyncResult. This is interesting. Just now I said that I will talk about the second peoperty of the IAsyncResult in a moment. Now is that moment. The second parameter is called AsyncWaitHandle.

The AsyncWaitHandle is of type WaitHandle, a class defined in the System.Threading namespace. WaitHandle class encapsulates a Handle (which is a pointer to int or handle ) and provides a way to wait for that handle to become signaled. The class has several static methods like WaitOne ( which is similar to WaitForSingleObject ) WaitAll ( similar to WaitForMultipleObjects with waitAll true ) , WaitAny etc. Also there are overloads of these functions available with timeouts.

Coming back to our discussion of IAsyncResult interface, the handle in AsyncWaitHandle (WaitHandle) is signalled when the receive operation completes. So if we wait on that handle infinitely we will be able to know when the receive completed. This means if we pass that WaitHandle to a different thread, the different thread can wait on that handle and can notify us of the fact that the data has arrived and so that we can read the data. So you must be wondering if we use this mechanism why would we use callback function. We won't. Thats right. If we choose to use this mechanism of the WaitHandle then the callback function parameter to the BeginReceive can be null as shown here:

//m_asynResult is declared of type IAsyncResult and assumming that m_socClient has made a connection.
m_asynResult = m_socClient.BeginReceive(m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,null,null);
if ( m_asynResult.AsyncWaitHandle.WaitOne () )
{
    int iRx = 0 ;
    iRx = m_socClient.EndReceive (m_asynResult);
    char[] chars = new char[iRx + 1];
    System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
    int charLen = d.GetChars(m_DataBuffer, 0, iRx, chars, 0);
    System.String szData = new System.String(chars);
    txtDataRx.Text = txtDataRx.Text + szData;
}

Even though this mechanism will work fine using multiple threads, we will for now stick to our callback mechanism where the system notifies us of the completion of asynchronous operation which is Receive in this case .

Lets say we made the call to BeginReceive and after some time the data arrived and our callback function got called.Now question is where's the data? The data is now available in the buffer that you passed as the first parameter while making call to BeginReceive() method . In the following example the data will be available in m_DataBuffer :

BeginReceive(m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,pfnCallBack,null);

But before you access the buffer you need to call EndReceive() function on the socket. The EndReceive will return the number of bytes received . Its not legal to access the buffer before calling EndReceive. To put it all together look at the following simple code:

byte[] m_DataBuffer = new byte [10];
IAsyncResult m_asynResult;
public AsyncCallback pfnCallBack ;
public Socket m_socClient;
// create the socket...
public void OnConnect()
{
    m_socClient = new Socket (AddressFamily.InterNetwork,SocketType.Stream ,ProtocolType.Tcp );
    // get the remote IP address...
    IPAddress ip = IPAddress.Parse ("10.10.120.122");
    int iPortNo = 8221;
    //create the end point
    IPEndPoint ipEnd = new IPEndPoint (ip.Address,iPortNo);
    //connect to the remote host...
    m_socClient.Connect ( ipEnd );
    //watch for data ( asynchronously )...
    WaitForData();
}
public void WaitForData()
{
    if ( pfnCallBack == null )
    pfnCallBack = new AsyncCallback (OnDataReceived);
    // now start to listen for any data...
    m_asynResult =
    m_socClient.BeginReceive (m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,pfnCallBack,null);
}
public void OnDataReceived(IAsyncResult asyn)
{
    //end receive...
    int iRx = 0 ;
    iRx = m_socClient.EndReceive (asyn);
    char[] chars = new char[iRx + 1];
    System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
    int charLen = d.GetChars(m_DataBuffer, 0, iRx, chars, 0);
    System.String szData = new System.String(chars);
    WaitForData();
}

The OnConnect function makes a connection to the server and then makes a call to WaitForData. WaitForData creates the callback function and makes a call to BeginReceive passing a global buffer and the callback function. When data arrives the OnDataReceive is called and the m_socClient's EndReceive is called which returns the number of bytes received and then the data is copied over to a string and a new call is made to WaitForData which will call BeginReceive again and so on. This works fine if you have one socket in you application.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“The first 90% of the code accounts for the first 90% of the development time. The remaining 10% of the code accounts for the other 90% of the development time.” - Tom Cargill