QuickConnect.NET

A lot of times we feel the need of adding a client/server based module in our software – a module that should be easy to add, manage, and dispose.

I wished for such a thing a lot of times. I thought I should add something for myself using C#. C# has hidden a lot of the back end functionality that was used to use during the Win32 days. I plan to write similar software that will run without any runtimes; So… keep checking the updates.

Introduction

Connect.NET is a C# component, that provides easy access to the TCP based network connections (Client/server). Basically there are two self explanatory classes, CServer and CClient, derived from CNetMgr. You will need to create CNetMgr’s object and specify IP, Port, and a bool that would tell if it’s a server or client.

Nevermind the “C” that I have prefixed my classes with, this is the old Visual C++ practice that I am into still now.

Server

A server is a running program on a networked computer that accepts requests from programs running on other computers to perform a service, and responds appropriately. Servers include IIS and Apache. A server accepts requests for a data/service from client software and returns the results to the client

Client

A client is the requesting processes.

How To Use

This is a three step process:

STEP 1: Declare the object

STEP 2: Define the type, port, and IP

STEP 3: Start the engine

Note that you also have the option to add events to know about new message arrivals.

Declaration

//Initialize NetMgr objects
   private CNetMgr m_theClient;  //Client object from NetMgr
   private CNetMgr m_theServer;  //Server, listens to clients' 

How Should I Run the Client?

To run the client, you’ll need to instantiate the m_theClient object by passing IP address and Port of the server; while the third argument(false) tells that it’s a client. Also you may (recommended) add an event that you tell you that a message has been received, so that you can work over the received message.

//Instantiate with the IP/Port and connection type (Server/client)
  m_theClient = new CNetMgr(lbtIP.Text,   //IP address to connect to
      Convert.ToInt32(lbtPort.Text),    //Server port
      false);   //False =       not
    server <

       BR >  //Add event to know about the new messages being arrived
    m_theClient.OnStatusChange += new CNetMgr.StatusChangeEventHandler(m_theClient_OnStatusChange);  //Start the client, lets connect.
    m_theClient.Start();  

How to run the server?

Instantiate m_theServer with CNetMgr by passing the IP, Port; adding a Boolean true would tell the CNetMgr to prepare a server. Similarly you can add the OnStatusChange to know about new message arrivals.

//Define the server settings, constructor
  m_theServer = new CNetMgr(lbtIP.Text,  //IP address of this server, usually, localhost (this machine)
         Convert.ToInt32(lbtPort.Text),  //Port to listen at
         true); //true =
    server <

    BR >   //Register to listen for the events of new messages arrived
    m_theServer.OnStatusChange += new CNetMgr.StatusChangeEventHandler(m_theServer_OnStatusChange); //Start the server, start listening
    m_theServer.Start(); 

Object Internals

The following discusses the internals of the client and server objects.

What does a client do internally?

A client internally creates a new TcpClient socket for the given IP and port, and uses a while loop to continue to try to connect to the server until it gets connected. While Wait() method performs a simple Sleep() call for the given amount of time.

private void Connector()
{
   m_theClient = new TcpClient(); 
IPEndPoint epServerID = new IPEndPoint(IPAddress.Parse(m_strIP),
Convert.ToInt32(m_nPort)); //Continue, until it gets connected. while (!m_theClient.Connected) { try { Log("Trying to connect... to: " + m_strIP + ":" + m_nPort.ToString()); m_theClient.Connect(epServerID); m_bIsConnected = true; break; //here, this means it got the connection successfully; lets do the rest. } catch (Exception exc) { Log("Couldnt find server."); CHelper.Log(exc.Message); } Wait(); //Wait for sometime. }
Log("Connected. [" + m_strIP + ":" + m_nPort.ToString() + "]"); m_nwkStream = m_theClient.GetStream(); m_thdClientHandler = new System.Threading.Thread(delegate() { HandleClient(); }); m_thdClientHandler.IsBackground = true; < BR > m_thdClientHandler.Start(); //Start a thread that will handle < BR > //all the tcp connection for this client. }

How the Client Process Is Handled

The client handler thread manages all the chat between server and the client, and look for the logout flag to disconnect the client gracefully.

 private void HandleClient()
 {
    ASCIIEncoding theEncoding = new ASCIIEncoding();
    byte[] bytMessage = new byte[READ_BUFFER_MAX_SIZE]; try
    {
      while (true)
      {
        int nBytesRead = 0; 
if (m_theClient == null || m_nwkStream == null) break;
if (!m_theClient.Connected) break;
if (m_nwkStream.CanRead && m_nwkStream ! = null) nBytesRead = m_nwkStream.Read(bytMessage, 0, bytMessage.Length); < BR > if (nBytesRead = = 0) continue; < BR > string strMsg = theEncoding.GetString(bytMessage); Log("MSG[" + m_strIP + ":" + m_nPort + "]" + strMsg); if (strMsg != null && strMsg == "logout") break; } } catch (Exception exc) { Error(exc.Message); } }

What happens when the server is started?

When the server is started, it listens for the incoming connection requests, and manages them in a list accordingly. For every new client connected, the server starts a new thread that manages the communication between the client and server.

private void Listen()
{
   try
   {
      m_theSvr = new TcpListener(IPAddress.Parse(m_strIP), Convert.ToInt32(m_nPort));
      m_theSvr.Start(NO_OF_MAX_CLIENTS_SUPPORTED); //the backlog, for now its 10
      m_bIsConnected = true; 
while (!m_bStopped) { Log("Listening..."); if (m_theSvr == null) break;
TcpClient thisClient = this.m_theSvr.AcceptTcpClient(); Log(thisClient.Client.LocalEndPoint.ToString() + ">>New entry... : [" + < BR > thisClient.Client.LocalEndPoint.ToString() + "]");//New connection m_lstClients.Add(thisClient); < BR > Thread thdManageClient = new Thread(new ParameterizedThreadStart(ManageClient)); thdManageClient.IsBackground = true; thdManageClient.Start(thisClient); //Start a new thread to manage. } } catch(Exception exc) { Error(exc.Message); } }

Manage client connection request

Since for each client a new thread is spawned (fork), this makes it less scalable, which basically is a disadvantage.

private void ManageClient(object theClient)
{
   TcpClient thisClient = (TcpClient)theClient;
   NetworkStream theComlayer = thisClient.GetStream();
   int nBytesRead = 0;
   ASCIIEncoding theEncoding = new ASCIIEncoding();
   while (true)
   {
      try
      {
         byte[] bytMessage = new byte[MSG_SIZE_4KB];
         nBytesRead = theComlayer.Read(bytMessage, 0, bytMessage.Length);
         if (nBytesRead == 0)
           continue; string strMsg = theEncoding.GetString(bytMessage);
         Log("\r\nRX: MSG[" + thisClient.Client.LocalEndPoint.ToString() + "]" + strMsg);
         if (strMsg.StartsWith("logout"))
         {
            bool bRemoved = m_lstClients.Remove(thisClient); //remove from the list
            break;
         }
      }
      catch (Exception exc)
      {
         Error(exc.Message);
         break;
      }
   }
}

Server Sending the Message

First the server looks for initial [IP:Port] tag in the outgoing message, then the server responds to the specific client who is supposed to get the message by browsing through the list of connected clients.

public int Send(string strData)
{
   int nBytesSent = 0;
   if (m_theSvr == null)
      return nBytesSent;
   try
   {
      int nIndexStart = strData.IndexOf("[");
      int nIndexEnd = strData.IndexOf("]");
      string strMsg = strData.Substring(1, nIndexEnd -1); //Cut the initial IP/Port tag out of the data. foreach (TcpClient theClient in m_lstClients)
      {
         try
         {
            if (!theClient.Client.RemoteEndPoint.ToString().Equals(strMsg))
               continue;
            if (theClient.Connected)
            {
               if (theClient.Client.Connected)
               {
                  nBytesSent = theClient.Client.Send(System.Text.ASCIIEncoding.ASCII.GetBytes(strData));
                  Log("\r\nTX: " + " [" + theClient.Client.RemoteEndPoint.ToString() + "] " + strData);
                  break;
               }
            }
         }
         catch (Exception exc)
         {
            Error(exc.Message);
         }
      }
   }
   catch(Exception exc)
   {
     Error(exc.Message);
   }
   return nBytesSent;
} 

Server, containing the list of connected clients

private List<TcpClient> m_lstClients = new List<TcpClient>(); [Category("Settings")]
[Description("Provides the list of connected clients")]
   [BrowsableAttribute(true)]
 public List<TcpClient> ListOfClients
   {
      get { return m_lstClients; }
   }
  

Events, notifying about the new messages

public delegate void StatusChangeEventHandler(string strData, bool bIsServer);
public event StatusChangeEventHandler OnStatusChange;
 

Enhancements Areas

1. Add scalability, only couple of threads manage all of connected clients.

[blog]: http://izlooite.blogspot.com

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read