You are in: Home > Articles > Asynchronous Socket Utility Classes

Asynchronous Socket Utility Classes

Article Links

Sample Application 1

Now that we are done constructing our Socket Client class, we need to test how it works.The first thing to do is open the Class1.cs file.Add the SocketSystem namespace so we can access our new class CSocketClient.

using SocketSystem;

Now add the following static public functions to the Class1 class after the static Main function.

//********************************************************************

/// <summary> Called when a message is extracted from the socket </summary>

/// <param name="pSocket"> The SocketClient object the message came from </param>

/// <param name="iNumberOfBytes"> The number of bytes in the RawBuffer inside the SocketClient </param>

static public void MessageHandlerClient(CSocketClient pSocket, Int32 iNumberOfBytes)

{

try

{

    // Convert the message from a byte array to a string

    String strMessage = System.Text.ASCIIEncoding.ASCII.GetString(pSocket.GetRawBuffer, 0, iNumberOfBytes);

       

    // Display the string to the console window

    Console.WriteLine(strMessage);

}

catch (Exception pException)

{

    Console.WriteLine(pException.Message);

}

}

   

//********************************************************************

/// <summary> Called when a socket connection is closed </summary>

/// <param name="pSocket"> The SocketClient object the message came from </param>

static public void CloseHandler(CSocketClient pSocket)

{

Console.WriteLine("Close Handler");

Console.WriteLine("IpAddress: " + pSocket.GetIpAddress);

}

  

//********************************************************************

/// <summary> Called when a socket error occurs </summary>

/// <param name="pSocket"> The SocketClient object the message came from </param>

/// <param name="pException"> The reason for the error </param>

static public void ErrorHandler(CSocketClient pSocket, Exception pException)

{

Console.WriteLine(pException.Message);

}

Implement a function called TestClient.I thought it would be fun to use the CSocketClient class to connect to a web server and pretend our application was a browser.So what we will do is connect to the web server hosting www.continuumtechnologycenter.com, issue a HTTP Get command, and see what comes back through the Message Handler.

//*******************************************************************

/// <summary> Function to test the CSocketClient class </summary>

static void TestClient()

{

try

{

    // Instantiate a CSocketClient object

    CSocketClient pSocketClient = new CSocketClient(10240, null,

      new CSocketClient.MESSAGE_HANDLER(MessageHandlerClient),

      new CSocketClient.CLOSE_HANDLER(CloseHandler),

      new CSocketClient.ERROR_HANDLER(ErrorHandler));

       

    // Generate the HTTP Get command to send to the server

    String strBrowserRequest = "GET / HTTP/1.1\n" +

      "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, */*\n" +

      "Accept-Language: en-us\n" +

      "Accept-Encoding: gzip, deflate\n" +

      "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)\n" +

      "Host: www.continuumtechnologycenter.com:80\n" +

      "Connection: Keep-Alive\n\n";

    // Establish a connection to the server hosting www.continuumtechnologycenter.com

    pSocketClient.Connect("www.continuumtechnologycenter.com", 80);

   // Send the HTTP Get command

    pSocketClient.Send(strBrowserRequest);

    Console.ReadLine();

    pSocketClient.Disconnect();

    Console.ReadLine();

   pSocketClient.Dispose();

}

catch (Exception pException)

{

    Console.WriteLine(pException.Message);

}

}

Finallly add the following code to the Main function.

//*******************************************************************

/// <summary> Function to test the CSocketClient class </summary>

static void Main(string[] args)

{

// Test the CSocketClient class

TestClient();

}

Build and run the application.You should get the following

C# Asynchronous Socket Utility Classes

So now you have the ability to add socket client support to your applications easily and quickly.In your sample application all you need to do is decide which server you want to connect to and code the handler functions appropriately.

CSocketServer

Now we will build a class that provides socket server support.

Setup CSocketServer Class

Open the UtilSocket.cs file and add the follow class just after the CSocketClient class inside the namespace.

} // End of CSocketClient

//========================================================================

///<summary> This class abstracts a socket server </summary>

public class CSocketServer

{

// Delegate Method Types

// Private Properties

// Public Properties

// Constructor, Finalize, Dispose

    //********************************************************************

///<summary>Constructor for client support </summary>

    public CSocketServer()

    {

    }

// Private Methods

// Public Methods

}

} // End of Namespace

Delegate Method Types Section

We need to add four delegate method types.

The first three delegate method types are identical to the delegate method types we added to the CSocketClient class and perform the same function.The new delegate method type is for the Accept Handler.We will call the method referenced by a property of this type when a client socket connection request is accepted.

/// <summary> DelType: Called when a message is extracted from the socket </summary>

public delegate void MESSAGE_HANDLER(CSocketClient pSocket, Int32 iNumberOfBytes);

/// <summary> DelType: Called when a socket connection is closed </summary>

public delegate void CLOSE_HANDLER(CSocketClient pSocket);

/// <summary> DelType: Called when a socket error occurs </summary>

public delegate void ERROR_HANDLER(CSocketClient pSocket, Exception pException);

/// <summary> DelType: Called when a socket connection is accepted </summary>

public delegate void ACCEPT_HANDLER(CSocketClient pSocket);

Private Properties Section

We need to add eleven private properties

In the CSocketClient class, we used the TcpClient class to provide the support we needed to establish socket connections to a server.In this class we will create a property using the TcpListener class.This property will provide the support we need to listen for and accept those client connections requests coming from the TcpClient class or similar technologies.

private TcpListener m_pTcpListener;

///<summary> RefType: A TcpListener object to accept socket connections </summary>

private TcpListener GetTcpListener { get { return m_pTcpListener; } set { m_pTcpListener = value; } }

The CSocketServer class will be responsible for listening and accepting client socket connection requests via the TcpListener property.We need a thread to perform this responsibility.   This property is a thread object to manage the Accept thread.

private Thread m_pAcceptThread;

///<summary> RetType: A thread to process accepting socket connections </summary>

private Thread GetAcceptThread { get { return m_pAcceptThread; } set { m_pAcceptThread = value; } }

Once a connection request is accepted the Accept thread, the thread will instantiate a CSocketClient object.The new CSocketClient object will be used to encapsulate the new socket connection.The CSocketServer will maintain an array of the CSocketClient objects it instantiates and will be responsible for the lifetime of these objects.

private CSocketClient[] m_pSocketClientList;

///<summary> RefTypeArray: An Array of SocketClient objects </summary>

private CSocketClient[] GetSocketClientList { get { return m_pSocketClientList; } set { m_pSocketClientList = value; } }

The application developer will decide the maximum number of client socket connections that will be accepted.This property maintains this number.It is used later in the constructor to instantiate the SocketClientList property array.

private Int32 m_iMaxClientConnections;

///<summary> SimType: Maximum number of client connections </summary>

public Int32 GetMaxClientConnections { get { return m_iMaxClientConnections; } set { m_iMaxClientConnections = value; } }

Since the Accept thread is instantiating CSocketClient objects, it must pass the required arguments to its constructor.This property is maintained by the CSocketServer class so the Accept thread can pass this value to the constructor of the CSocketClient objects it creates.

private Int32 m_iSizeOfRawBuffer;

///<summary> SimType: Size of the raw buffer for received socket data </summary>

private Int32 GetSizeOfRawBuffer { get { return m_iSizeOfRawBuffer; } set { m_iSizeOfRawBuffer = value; } }

The following delegate method type properties are maintained because they need to be passed to the constructor of the CSocketClient objects created by the Accept thread.The Accept Handler property is used by the Accept thread and not passed to the instantiated CSocketClient object.When the Accept thread accepts a client socket connection request and instantiates a CSocketClient object, the Accept thread will call the Accept Handler method.This is when the application developer can perform any application specific actions required when a client connection is accepted.

private MESSAGE_HANDLER m_pfnMessageHandler;

/// <summary> DelType: A reference to a user supplied function to be called when a socket message arrives </summary>

private MESSAGE_HANDLER GetMessageHandler { get { return m_pfnMessageHandler; } set { m_pfnMessageHandler = value; } }

          

private CLOSE_HANDLER m_pfnCloseHandler;

/// <summary> DelType: A reference to a user supplied function to be called when a socket connection is closed </summary>

private CLOSE_HANDLER GetCloseHandler { get { return m_pfnCloseHandler; } set { m_pfnCloseHandler = value; } }

      

private ERROR_HANDLER m_pfnErrorHandler;

/// <summary> DelType: A reference to a user supplied function to be called when a socket error occurs </summary>

private ERROR_HANDLER GetErrorHandler { get { return m_pfnErrorHandler; } set { m_pfnErrorHandler = value; } }

private ACCEPT_HANDLER m_pfnAcceptHandler;

/// <summary> DelType: A reference to a user supplied function to be called when a socket connection is accepted </summary>

private ACCEPT_HANDLER GetAcceptHandler { get { return m_pfnAcceptHandler; } set { m_pfnAcceptHandler = value; } }

There will be a need for synchronization because of the SocketClientList array.Different threads will need to add and remove CSocketClient object references from this array at possibly the same time.This object property will be used in a Monitor object to provide critical section support to the required member methods.

private Object m_pSocketListCS;

///<summary> RefType: A synchronization object to protect the class state </summary>

private Object GetSocketListCS { get { return m_pSocketListCS; } set { m_pSocketListCS = value; } }

The last private property is the dispose flag property.The finalize method will use this flag to determine if the class has already been disposed or not.

private Boolean m_bDisposeFlag;

///<summary> SimType: Flag to indicate if the class is disposing </summary>

private Boolean IsDisposed { get { return m_bDisposeFlag; } set { m_bDisposeFlag = value; } }

Public Properties Section

We need to add three public properties.

This property maintains the ipaddress of a local nic card used to listen for client socket connections.If you want to listen on multiple nic cards you will need to instantiate multiple CSocketServer objects, one for each card.This property can store a host name or an ipaddress.If this value is a host name it must be resolvable by a DNS lookup.

private String m_strIpAddress;

///<summary> RefType: The IpAddress to either connect to or listen on </summary>

public String GetIpAddress { get { return m_strIpAddress; } set { m_strIpAddress = value; } }

This property maintains the port the server is to listen for client socket connections on.You must make sure the port is currently not in use.To manually check if a port is in use, run the "netstat -a " command from the command line.

private Int16 m_iPort;

///<summary> SimType: The Port to either connect to or listen on </summary>

public Int16 GetPort { get { return m_iPort; } set { m_iPort = value; } }

This property is for the application developer.If the application developer would like to store some state associated with the socket server, he can use this property.

private Object m_pUserArg;

///<summary> RefType: A reference to a user defined object to be passed through the handler functions </summary>

public Object GetUserArg { get { return m_pUserArg; } set { m_pUserArg = value; } }

Constructor, Finalize, and Dispose Section

In the constructor, all we need to do is instantiate the object for the critical section property and set the dispose property flag to false.

//********************************************************************

/// <summary> Constructor </summary>

public CSocketServer()

{

// Create the critical section object

GetSocketListCS = new Object();

             

// Init the dispose flag

IsDisposed = false;

}

The finalize method verifies that the object instance has not been disposed already.If the application developer remembers properly to call dispose, there is nothing for finalize to do.This finalize function only exists as a safe guard.

                               

//********************************************************************

/// <summary> Finalize </summary>

~CSocketServer()

{

// If this object has not been disposed yet

if (!IsDisposed)

    Stop();

}

The dispose method sets the dispose flag to true so the finalize function does not try to dispose the object instance again.All that needs to be done is to make sure the Accept thread is terminated.The Stop method performs that action as you will see later in the public methods section.

                               

//********************************************************************

/// <summary> Dispose function to shutdown the SocketManager </summary>

public void Dispose()

{

try

{

    // Mark the object as disposed

    IsDisposed = true;

              

    // Stop the server if the thread is running

    if (GetAcceptThread != null)

      Stop();

}

catch

{

}

}

Part 3: Asynchronous Socket Utility Classes: Private & Public Methods


by William Kennedy

About the Author

William Kennedy is a graduate of SUNY Potsdam in Potsdam NY and a member of Sigma Pi fraternity. William works for Concerto Software, which provides contact center solutions that help companies more effectively manage customer interactions across all channels. At Concerto Software he is responsible for the architect, design, and implementation of real-time server based software written in both C++ and C#. William is also the co-owner of Continuum Technology Center. The Continuum provides articles and source code to help developers develop better products in C#. When William is not working, you can find him playing with his five kids.