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.

Comments
Not Success
Posted by fanjiehao886 on 07/19/2009 11:14pmCan you give me an example using this code ,it will be greate appreciated!!!
ReplyMissing Classes
Posted by pnovosel on 05/05/2009 10:29pmWhen I try to build a example using your Code, it's missing CHelp. Do you have a Working Test Program (Source).
-
ReplyCHelper is optional
Posted by jusstujoo on 07/05/2009 11:35ampnovosel, 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