Home arrow C# arrow Page 2 - Asynchronous Socket Utility Classes – Part II
C#

Asynchronous Socket Utility Classes – Part II


This is part two of William's series on using Asynchronous Socket Utility Classes in C#. In this article, William will describe the code for the server side. Once you have read the two parts you should have no troubles with these classes.

Author Info:
By: William Kennedy
Rating: 5 stars5 stars5 stars5 stars5 stars / 22
March 31, 2003
TABLE OF CONTENTS:
  1. · Asynchronous Socket Utility Classes – Part II
  2. · Component Design and Coding
  3. · Sample Application Update
  4. · Review

print this article
SEARCH DEVARTICLES

Asynchronous Socket Utility Classes – Part II - Component Design and Coding
(Page 2 of 4 )

CSocketServer

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

Open the UtilSocket.cs file created during Part I of this article 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
  {
  }
}

Private Methods Section

We need to add two private methods.

The LocateSocketIndex method is used to locate an empty slot in the SocketClientList property array.  This is used by the Accept thread to save a reference to a newly instantiated CSocketClient object.
 
In the LocateSocketIndex method, a local simple type variable called iSocket is set to the max number of client connections the CSocketServer object is allowed to accept.  This is because the method returns the value of iSocket at the end of the method.  If no slots are available, we need to return the max client connections value to indicate the maximum number of client connections currently exist.

Next, we wait to enter the Monitor object.  We only want one thread accessing the SocketClientList property array at a time.  In the call to Monitor.Enter, we pass the SocketList object property to protect the SocketClientList property array.   Now we safely search the SocketClientList property array for an empty slot.  Once we find an empty slot, we break out of the loop.  After the loop, we call Monitor.Exit passing the SocketList object property again and return the value of iSocket.

Notice how the calls to Monitor.Enter and Monitor.Exit are outside the try and catch statements.  This is done to make sure that if we successfully enter the critical section we absolutely exit from it.  If you do not call Monitor.Exit after passing through Monitor.Enter, no other threads will ever get through the critical section again.  Those threads waiting to pass through the Monitor.Enter call will block forever.  It is important to always make sure Monitor.Exit is called and only called once.  I have found this to be the most efficient and safest way to guarantee critical sections get released properly.

//*************************************************
/// <summary> Function to locate an empty slot in the client socket list array </summary>
/// <returns> Will return the index slot or user defined MaxClientConnections if none found </returns>
private Int32 LocateSocketIndex()
{
  Int32 iSocket = GetMaxClientConnections;
  Monitor.Enter(GetSocketListCS);
  try
  {
    // Find an empty slot in the list
    for (iSocket = 0; iSocket < GetMaxClientConnections; ++iSocket)
      if (GetSocketClientList[iSocket] == null)
        break;
  }  
  catch
  {
  }
  Monitor.Exit(GetSocketListCS);
  return iSocket;
}

The AcceptThread method is spawned as a worker thread.  It performs most of the work for the CSocketServer class.  The job of the AcceptThread method is to listen for incoming client socket connection requests and accept those requests.  Once a client socket connection request is accepted by the call to the TcpListener AcceptSocket method, the Accept thread instantiates a CSocketClient object. 

The new CSocketClient object exists to encapsulate the accepted socket connection.  We then store a reference to the CSocketClient object in the SocketClientList property array.  Once these activities are complete, then we call the Accept Handler.  This allows the application developer to be notified a client socket connection request has been accepted.

In both of the catch statements, we call the Error Handler.  This notifies the application developer to be notified about an error accepting a socket client connection.  In the catch statement that catches the System.Net.Sockets.SocketException object, we check for error code 10004. 

This error code means that a socket blocking call was cancelled.  We get this error when the TcpListener property is stopped.  In this case, we do not call the Error Handler since this is a known error that is unavoidable.  In both catch statements, we close the socket client connection if we accepted the connection before the error.

//************************************************
/// <summary> Function to process and accept socket connection requests </summary>
private void AcceptThread()
{
  Socket pClientSocket = null;
  try
  {
    // Create a new TCPListner and start it up
    GetTcpListener = new TcpListener(Dns.Resolve(GetIpAddress).AddressList[0],GetPort);
    GetTcpListener.Start();
    for (;;)
    {
      // If a client connects, accept the connection
      pClientSocket = GetTcpListener.AcceptSocket();
      if (pClientSocket.Connected)
      {
        // Locate a socket index
        Int32 iSocketIndex = LocateSocketIndex();
        // If we got a valid index
        if (iSocketIndex != GetMaxClientConnections)
        {
          // Create a SocketClient object
          GetSocketClientList[iSocketIndex] = new CSocketClient(this,
            pClientSocket,                                           // The socket object for the connection
            iSocketIndex,                                            // The index into the SocketClientList
            pClientSocket.RemoteEndPoint.ToString().Substring(0,15), // The IpAddress of the client
            GetPort,                                                 // The port the client connected to
            GetSizeOfRawBuffer,                                      // The size of the byte array for storing messages
            GetUserArg,                                              // Application developer state
            new CSocketClient.MESSAGE_HANDLER(GetMessageHandler),    // Application developer Message Handler
            new CSocketClient.CLOSE_HANDLER(GetCloseHandler),        // Application developer Close Handler
            new CSocketClient.ERROR_HANDLER(GetErrorHandler));       // Application developer Error Handler
          // Call the Accept Handler
          GetAcceptHandler(GetSocketClientList[iSocketIndex]);
        }
        else
        {
          // Call the Error Handler
          GetErrorHandler(null, new Exception("Unable To Accept Socket Connection"));
          // Close the socket connection
          pClientSocket.Close();
        }
      }
    }
  }
  catch (System.Net.Sockets.SocketException e)
  {
    // Did we stop the TCPListener
    if (e.ErrorCode != 10004)
    {
      // Call the error handler
      GetErrorHandler(null, e);
      // Close the socket down if it exists
      if (pClientSocket != null)
        if (pClientSocket.Connected)
          pClientSocket.Close();
    }
  }
  catch (Exception e)
  {
    // Call the error handler
    GetErrorHandler(null, e);
    // Close the socket down if it exists
    if (pClientSocket != null)
      if (pClientSocket.Connected)
        pClientSocket.Close();
  }
}

Public Methods Section

We need to add one public method.

The RemoveSocket method is used to remove a reference of a CSocketClient object from the SocketClientList property array.  A reference to a CSocketClient object is passed to the RemoveSocket method.  We first need to check to make sure the reference to the passed CSocketClient object is valid.  We do this by comparing the reference passed into RemoveSocket with the reference located in the SocketClientList property array.  We determine which slot to look at in the SocketClientList array by using the slot the passed CSocketClient object says it’s located in. 

If these references match, we de-reference the object from the SocketClientList property array.  You may have noticed that the CSocketClient class currently does not have a public property called GetSocketListIndex.  We will be adding the public property to the CSocketClient class later.  Again the critical section is used to protect the SocketClientList property array.  The calls to Monitor.Enter and Monitor.Exit are outside the try and catch statements.

//*********************************************
/// <summary> Funciton to remove a socket from the list of sockets </summary>
/// <param name="iSocketListIndex"> SimType: The index of the socket to remove </param>
public void RemoveSocket(CSocketClient pSocketClient)
{
  Monitor.Enter(GetSocketListCS);
  try
  {
    // Is the supplied CSocketClient object valid
    if (pSocketClient == GetSocketClientList[pSocketClient.GetSocketListIndex])
    {
      // Set the slot to null
      GetSocketClientList[pSocketClient.GetSocketListIndex] = null;
    }
  }
  catch
  {
  }
  Monitor.Exit(GetSocketListCS);
}

//***********************************************
/// <summary> Function to start the SocketServer </summary>
/// <param name="strIpAddress"> RefType: The IpAddress to listening on </param>
/// <param name="iPort"> SimType: The Port to listen on </param>
/// <param name="iMaxClientConnections"> SimType: Max number of client connections accepted </param>
/// <param name="iSizeOfRawBuffer"> SimType: Size of the Raw Buffer </param>
/// <param name="pUserArg"> RefType: User supplied arguments </param>
/// <param name="pfnMessageHandler"> DelType: Function pointer to the user MessageHandler function </param>
/// <param name="pfnAcceptHandler"> DelType: Function pointer to the user AcceptHandler function </param>
/// <param name="pfnCloseHandler"> DelType: Function pointer to the user CloseHandler function </param>
/// <param name="pfnErrorHandler"> DelType: Function pointer to the user ErrorHandler function </param>
public void Start(String strIpAddress, Int16 iPort, Int32 iMaxClientConnections, Int32 iSizeOfRawBuffer, Object pUserArg,
                  MESSAGE_HANDLER pfnMessageHandler, ACCEPT_HANDLER pfnAcceptHandler, CLOSE_HANDLER pfnCloseHandler,
                  ERROR_HANDLER pfnErrorHandler)
{
  // Is an AcceptThread currently running
  if (GetAcceptThread == null)
  {
    // Set connection values
    GetIpAddress            = strIpAddress;
    GetPort                 = iPort;
    GetMaxClientConnections = iMaxClientConnections;
       
    // Init the array of CSocketClient references
    GetSocketClientList = new CSocketClient[iMaxClientConnections];
       
    // Save the Handler Functions
    GetMessageHandler = pfnMessageHandler;
    GetAcceptHandler  = pfnAcceptHandler;
    GetCloseHandler   = pfnCloseHandler;
    GetErrorHandler   = pfnErrorHandler;
       
    // Save the buffer size and user arguments
    GetSizeOfRawBuffer = iSizeOfRawBuffer;
    GetUserArg         = pUserArg;
       
    // Start the listening thread if one is currently not running
    ThreadStart tsThread = new ThreadStart(AcceptThread);
    GetAcceptThread = new Thread(tsThread);
    GetAcceptThread.Name = "Accept";
    GetAcceptThread.Start();
  }
}
   
//*********************************************
/// <summary> Function to stop the SocketServer.  It can be restarted with Start </summary>
public void Stop()
{
  // Abort the accept thread
  if (GetAcceptThread != null)
  {
    GetTcpListener.Stop();
    GetAcceptThread.Join();
    GetAcceptThread = null;
  }
   
  // Dispose of all of the socket connections
  for (int iSocket = 0; iSocket < GetMaxClientConnections; ++iSocket)
  {
    if (GetSocketClientList[iSocket] != null)
    {
      GetSocketClientList[iSocket].Dispose();
      GetSocketClientList[iSocket] = null;
    }
  }
     
  // Wait for all of the socket client objects to be destroyed
  GC.Collect();
  GC.WaitForPendingFinalizers();
     
  // Clear the Handler Functions
  GetMessageHandler = null;
  GetAcceptHandler  = null;
  GetCloseHandler   = null;
  GetErrorHandler   = null;
     
  // Clear the buffer size and user arguments
  GetSizeOfRawBuffer = 0;
  GetUserArg         = null;
}

CSocketClient Updates

Now we will add some new properties and methods to the CSocketClient class to support the CSocketServer class.

Private Properties Section

We need to add one private property.

This property maintains a reference back to the owning CSocketServer object.  We need this reference to call the CSocketServer RemoveSocket method from the CSocketClient Dispose method.

private CSocketServer m_pSocketServer;
  /// <summary> RefType: The SocketServer for this socket object </summary>
  private CSocketServer GetSocketServer { get { return m_pSocketServer; } set { m_pSocketServer = value; } }

Public Properties Section

We need to add two public properties.

This property maintains the index where this object is located in the CSocketServer SocketClientList array.

private Int32 m_iSocketListIndex;
  /// <summary> SimType: Index into the Socket List Array </summary>
  public Int32 GetSocketListIndex { get { return m_iSocketListIndex; } set { m_iSocketListIndex = value; } }

This property maintains a reference to the socket object created by the TcpListener property when the CSocketServer Accept thread accepts a client socket connection request.  This property will be passed to the NetworkStream property instead of the TcpClient property when the CSocketServer class instantiates a CSocketClient object.

private Socket m_pClientSocket;
  /// <summary> RefType: The socket for the client connection </summary>
  public Socket GetClientSocket { get { return m_pClientSocket; } set { m_pClientSocket = value; } }

Constructor, Finalize, and Dispose Section

We need to add a new constructor method to support the CSocketServer class.  This overloaded constructor method takes 10 arguments.  The XML documentation for the constructor describes the arguments.  In the constructor, we are setting the class properties that should not change during the lifetime of an object instance of this class.

In this version of the constructor method, we need to do all of the things we did as in the first version plus a few new things.  First, we need to save the reference to the CSocketServer object that created this CSocketClient object.  Next, we save the reference to the socket for the accepted connection and the index where this object can be located in the CSocketServer SocketClientList array.  Now we need to save the ipaddress and port of the client that connected to us.

In the CSocketClient Connect method we made the following call.

GetNetworkStream = GetTcpClient.GetStream();

This associated the new socket connection we established with the NetworkStream property so we could process socket messages asynchronously.  The Connect method will never be called in this context because the connection is already established.  In this case, we need to associate the socket being passed in with the NetworkStream property.

GetNetworkStream = new NetworkStream(GetClientSocket);

This statement above is used here in the constructor to associate the socket being passed in with the NetworkStream property.

The last thing to add is the socket options.  Since the Connect method will never be called, we need to set the socket options here.  We set all the same options we did in connect.

//**********************************************
/// <summary> Constructor for SocketServer Suppport </summary>
/// <param name="pSocketServer"> RefType: A Reference to the parent SocketServer </param>
/// <param name="pClientSocket"> RetType: The Socket object we are encapsulating </param>
/// <param name="iSocketListArray"> SimType: The index of the SocketServer Socket List Array </param>
/// <param name="strIpAddress"> RetType: The IpAddress of the remote server </param>
/// <param name="iPort"> SimType: The Port of the remote server </param>
/// <param name="pfnMessageHandler"> DelType: Reference to the user defined message handler function </param>
/// <param name="pfnCloseHandler"> DelType: Reference to the user defined close handler function </param>
/// <param name="pfnErrorHandler"> DelType: Reference to the user defined error handler function </param>
/// <param name="iSizeOfRawBuffer"> SimType: The size of the raw buffer </param>
/// <param name="pUserArg"> RefType: A Reference to the Users arguments </param>
public CSocketClient(CSocketServer pSocketServer, Socket pClientSocket, Int32 iSocketListArray, String strIpAddress, Int16 iPort,
                     Int32 iSizeOfRawBuffer, Object pUserArg, MESSAGE_HANDLER pfnMessageHandler, CLOSE_HANDLER pfnCloseHandler,
                     ERROR_HANDLER pfnErrorHandler)
{
  // Create the raw buffer
  GetSizeOfRawBuffer = iSizeOfRawBuffer;
  GetRawBuffer = new Byte[GetSizeOfRawBuffer];
  // Save the user argument
  GetUserArg = pUserArg;
  // Set the handler functions
  GetMessageHandler = pfnMessageHandler;
  GetCloseHandler   = pfnCloseHandler;
  GetErrorHandler   = pfnErrorHandler;
  // Set the async socket function handlers
  GetCallbackReadFunction = new AsyncCallback(ReceiveComplete);
  GetCallbackWriteFunction = new AsyncCallback(SendComplete);
  // Init the dispose flag
  IsDisposed = false;
  // Set reference to SocketServer
  GetSocketServer = pSocketServer;
  // Init the socket references
  GetClientSocket    = pClientSocket;
  GetSocketListIndex = iSocketListArray;
  // Set the Ipaddress and Port
  GetIpAddress = strIpAddress;
  GetPort      = iPort;
  // Init the NetworkStream reference
  GetNetworkStream = new NetworkStream(GetClientSocket);
  // Set these socket options
  GetClientSocket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.ReceiveBuffer, 1048576);
  GetClientSocket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.SendBuffer, 1048576);
  GetClientSocket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.DontLinger, 1);
  GetClientSocket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Tcp,    System.Net.Sockets.SocketOptionName.NoDelay, 1);
  // Wait for a message
  Receive();
}

We need to make one change to the Dispose method.  We need to add the call to the CSocketServer RemoveSocket method.

// Remove the socket from the list
if (GetSocketServer != null)
  GetSocketServer.RemoveSocket(this);

This is added after the catch statement to guarantee the call is made.  This will remove the reference to this object from the CSocketServer SocketClientList array.

//********************************************
/// <summary> Dispose </summary>
public void Dispose()
{
  try
  {
    // Flag that dispose has been called
    IsDisposed = true;
    // Disconnect the client from the server
    Disconnect();
  }
  catch
  {
  }
  // Remove the socket from the list
  if (GetSocketServer != null)
    GetSocketServer.RemoveSocket(this);
}

The last change is to the Disconnect method.  Here we need to add the following code.

if (GetClientSocket != null)
    GetClientSocket.Close();
GetClientSocket = null;

This code will close the socket and remove the reference this class has to the memory so garbage collection can clean up.

//********************************************
/// <summary> Function used to disconnect from the server </summary>
public void Disconnect()
{
  // Close down the connection
  if (GetNetworkStream != null)
    GetNetworkStream.Close();
  if (GetTcpClient != null)
    GetTcpClient.Close();
  if (GetClientSocket != null)
    GetClientSocket.Close();
  // Clean up the connection state
  GetClientSocket  = null;
  GetNetworkStream = null;
  GetTcpClient     = null;
}


blog comments powered by Disqus
C# ARTICLES

- Introduction to Objects and Classes in C#, P...
- Visual C#.NET, Part 1: Introduction to Progr...
- C# - An Introduction
- Hotmail Exposed: Access Hotmail using C#
- Razor Sharp C#
- Introduction to Objects and Classes in C#
- Making Your Code CLS Compliant
- Programming with MySQL and .NET Technologies
- Socket Programming in C# - Part II
- Socket Programming in C# - Part I
- Creational Patterns in C#
- Type Conversions
- Creating Custom Delegates and Events in C#
- Inheritance and Polymorphism
- Understanding Properties in C#

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials