TCP/IPLib Sample


Introduction

This compopnent contains both a TCP/IP server and client.

TcpIpClient inherits from System.Net.Sockets.TCPClient and is very straight forward. After construction, specifying the host and port, a network connection is established by calling Connect(). Internally, upon a successful connection, the client receives a TcpIpPacket, which contains a unique cookie that is used in any subsequent transactions. The actual receiving of data packets is done on a separate thread. The thread method simply goes into a do/while loop until a null reference is received from the server or the client disconnects the session.

TcpIpServer is a bit more complex, in that it is capable of hosting multiple clients. It performs connection acceptances on a separate thread using System.Net.Sockets.TCPListener. Multiple hosting is provide by TcpIpServerClient instances stored in a System.Collections.Hashtable. Each instance funnels packets through the server, which fires a received packet event.

This sample consists of three projects, 2 exes and a dll. The executables are the GUIs that make use of the TCP/IP component. Both have a manager (not shown), which allows 1..n dialogs, depending on screen width, server/clients to be created.

GUI Server



Enter the desired port number in the manager's editbox and hit the launch button. The manager remains visible so that other servers can be kicked off or killed.

GUI Client



Create the required number of clients in the same manner as was done with the server. The client manager hides itself once the clients have been launched.

Source Code

The entrypoint, after construction, to start the server accepting connections is the StartListening() method. Internally it creates a thread and give ThreadStart a reference to this.ListenFunc method. The meat of the component takes place in this method.

private void ListenFunc()
{
evtStart.Set();
int iRet = 0;
iRet = socketListener.Start();
try
{
Socket sock =
null;
while( (sock = socketListener.Accept()) != null)
{
TcpIpServerClient client =
null;
TcpIpPacket pack =
null;
int iClientId = (iServerId + (++iNextClientId));
client =
new TcpIpServerClient( this, iClientId, sock);
tcpServerClientColl.Add( iClientId, client);
//kick off the receiving thread
client.StartReceiving();
pack =
new TcpIpPacket( iPort, iClientId, 0);
pack.ByteBuf = TcpIpPacketHelper.StringToByteArray( "Server Init");
byte[] bytePack = null;
bytePack = TcpIpPacketHelper.PacketToByteArray( pack);
int bytesSent = 0;
bytesSent = sock.Send( bytePack, bytePack.Length, SocketType.SockStream);
if( bytesSent < 1)
{
TcpIpGeneralExc exc =
null;
exc =
new TcpIpGeneralExc( "Client connection send", bytesSent);
throw( exc);
}
NotifyConsumer( client);
}
}
catch( Exception exc)
{
if( OnWorkerThreadEvent != null)
{
WorkerThreadEventArgs args =
null;
args =
new WorkerThreadEventArgs( "An exception was catch by the server's listening thread. The thread has been aborted", exc, WorkerThreadEventArgs.ThreadSource.Server);
OnWorkerThreadEvent(
this, args);
} }
This won't be described in .NET best pratices, but being an old C/C++ hacker I use "null" testing frequently and have been bitten a number of times with a NullReferenceException. It would probably be a good idea to kick the habit.
threadStart =
null;
}
//method

Notes:

The clients have only been tested using a "localhost" connection. On multiple machines, there could be thread sync problems. I've tried to address this using multi-threading, AutoResetEvents and monitors, but no guarantees.

When creating servers, certain ports, 3 and 13 on my machince, cause the TCPListener to throw an exception. I haven't a clue why. That's the reason for the OnWorkerThreadEvent kludge. I could have done a number of things, but... The ideal solution would be some equivalent of a PostThreadMessageEvent.

During testing I discovered that connecting a client to a server, then dropping the connection and stopping the server's listening function, then trying to reconnect the same client to the same server would cause problems. The TcpIpClient.base.Connect( IPAddress, IntPort) would return success, but the fact was that the connection was invalid and trying to send data to the server woould result in an exception. The problem is time related, because after a half minute, or so, the reconnection works. I can only think that the runtime's GC cleanup is involved.