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


About the Author

KMan KMan

http://izlooite.blogspot.com

Downloads

Comments

  • Not Success

    Posted by fanjiehao886 on 07/19/2009 11:14pm

    Can you give me an example using this code ,it will be greate appreciated!!!

    Reply
  • Missing Classes

    Posted by pnovosel on 05/05/2009 10:29pm

    When I try to build a example using your Code, it's missing CHelp. Do you have a Working Test Program (Source).

    • CHelper is optional

      Posted by jusstujoo on 07/05/2009 11:35am

      pnovosel, Thanks for trying it out. CHelper is a helping class; basically contains generic methods; like for instance, CHelper.Log("Some text message"), this just shows the message. You can simply comment the messages or CHelper related methods. Let me know if this does not work for you; I'll send you updated QuickConnect.NET and CHelper class. Thanks

      Reply
    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • This paper introduces IBM Java on the IBM PowerLinux 7R2 server and describes IBM's implementation of the Java platform, which includes IBM's Java Virtual Machine and development toolkit.

  • Not all enterprise applications are created equal. Sophisticated applications need developer support but other more basic apps do not. With the right tools, everyone is a potential app developer with ideas and a perspective to share. Trends such as low-code development and model driven development are fundamentally changing how and who creates applications. Is your organization ready? Read this report and learn: The seven personas of enterprise app delivery How application ownership is spreading to the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds