C# TCP/IP Networking Client/Server Library - CodeProject

:

NETCOM Demo Sample Screenshot

Introduction

This library is aimed to make TCP/IP and Client/Server programming so easy. With its multi-threaded structure, it provides methods and properties to send and receive data between server and client using TCP protocol.

You will be able to easily develop your own Client/Server program without needing deep knowledge about socket programming. create your own command list, send and receive packets easily.

In addition, handshaking between client and server has been made very easy, just call handshake and the library will do the rest. This library is written in C# and uses no external dependency.

To Be Completed

This class is not developed to transfer large amount of data and it only works in TCP mode. I am working on it to support multi-packet data to support large data transfer automatically but unfortunately I am a little busy right now.

To send this type of data, you need to develop or modify this class to support UDP protocol.

Background

Please pay very attention to the following two parameters which are defined in NETCOM class:

MAX_TRY_COUNT

which means how many times the underlaying layer should re-try to send a packet or data to the other side before it returns failure.

DEFAULT_CONNECT_WAIT_TIMEOUT

which means how long the underlaying layer should wait for the connection to be completed with the other side before it returns failure.

MAX_PACKET_CONTENTS_LENGTH

  • NETCOM: Is the underlaying layer to provide connection to the other side and handle send/receive data between two sides.
  • AgentRelay: Is the main class that provides Packet based communication between two sides. Please pay attention that the maximum packet's data length is 400 bytes.
  • RawAgentRelay: Is the main class that provides communication between two sides in raw mode. You can send/receive data between two sides and the length of data can be different and then there is no control over the contents. Just consider the maximum data length is 2000 bytes.

NOTE: You can change the maximum data length in NETCOMM class directly.

Using the Code

There are two classes which are provided by this library:

  • AgentRelay: which is based on Packet transmit/receive system and is very useful to develop applications which require to be connected to each other to send and receive fixed commands over network.
  • RawAgentRelay: which is not based on Packet and transmit/receive raw bytes between two sides. This class is very useful when you want to develop applications that may work as a bridge and it should not pay attention whether what is exactly transferring between them.
  • ServerRelay: which is responsible to provide server functionality to accept and handle connection from another AgentRelay objects.

Note: Both AgentRelay and RawAgentRelay act as client modules and ServerRelay acts as a server module.

AgentRelay Class as Client Module

This class uses NETCOM to create a TCP/IP connection between two sides and provides Packet based communication between them including automatic handshake process.

Packet structure is shown here:

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi, Size = 406)]
public class Packet
{
    // Header
    [MarshalAs(UnmanagedType.I1)]
    public byte Type=0;                             // Reserved
    [MarshalAs(UnmanagedType.I2)]
    public ushort FragmentIndex=0;                  // Reserved
    [MarshalAs(UnmanagedType.I1)]
    public byte Command;                            // Command code
    [MarshalAs(UnmanagedType.I2)]
    public ushort DataLength;                       // Size of data to follow
    
    // Data
    // NOTE: IF YOU CHANGE THE SIZE, YOU HAVE TO CHANGE Constants.PacketLength too
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 400)]
    public byte[] Content;
}

In our example, we want to create a simple chat software to send/receive text messages between two sides. First, we need to create our own command list:

private enum eTcpCommands                            // Contents.. (Parameters)
{
    ChatMessage = 1,
    QueryNameRequest = 2,
    QueryNameResponse = 3,
    ....
}

NOTE: Command codes 250 to 255 are reserved for internal control process.

Create an instance of AgentRelay class and connect to other side using Connect method:

AgentRelay m_chatProvider = new AgentRelay();
m_chatProvider.Connect(tbIPAddress.Text, 1234);

To send message to other side, easily call SendMessage method:

m_chatProvider.SendMessage((int)eTcpCommands.ChatMessage, txt);

You need to handle received messages:

m_chatProvider.OnNewPacketReceived += chatProvider_OnNewPacketReceived;
private void chatProvider_OnNewPacketReceived(AgentRelay.Packet packet, AgentRelay agentRelay)
{
    switch(packet.Command)
    {
        case (byte)eTcpCommands.ChatMessage:
            this.Invoke(new MethodInvoker(() => { lbMessages.Items.Insert
            	(0,AgentRelay.MakeStringFromPacketContents(packet)); }));
            break;
            
        case (byte)eTcpCommands.QueryNameRequest:
            agentRelay.SendMessage((int)eTcpCommands.QueryNameResponse, "TCP/IP Test");
            break;
            
        case (byte)eTcpCommands.QueryNameResponse:
            this.Invoke(new MethodInvoker(() => { lbMessages.Items.Insert
            	(0, "NAME: " + AgentRelay.MakeStringFromPacketContents(packet)); }));
            break;
            
        default:
            agentRelay.SendResponse(AgentRelay.eResponseTypes.InvalidCommand);
            break;
    }
}

Finally, RawAgentRelay is same as AgentRelay but is not limited to packet concept.

Some Notes

  • SendMessage()

    This method is used to send your specific commands and contents to the other side.

  • SendResponse()

    This method is used to send fixed responses to the other side without any extra content, which are Success, Wait, Invalid and Error.

  • OnNewPacketReceived(AgentRelay.Packet packet, AgentRelay agentRelay)

    This event is called whenever a new packet is received, agentRelay refers to the sender client and you can use this object to send any response to the other side immediately.

  • GetNextReceivedPacket(out Packet packet, int timeoutMs)

    If you like to check for any newly received packet in your main code (instead by AgentRelay itself), you first must set the OnNewPacketReceived event to null and then periodically call GetNextReceivedPacket to receive packets if any.

  • UserData Property

    There is an object called UserData in AgentRelay class which is not used by the class directly and you can use it to store any type of data into your agentRelay object.

Note: When you are done, you have to call Dispose() to release all the allocated resources.

ServerRelay Class as Server Module

This class uses NETCOM to receive and handle a TCP/IP connection request and provides it as an AgentRelay object to you.

The first step is to create an object from ServerRelay class and call StartServer method. This method has two parameters which indicates the listening IP address and port. The IP address is optional and if passed as null, it will start listening on all network devices in the system (which is not recommended).

ServerRelay m_serverRelay = new ServerRelay(true);
m_serverRelay.OnNewAgentConnected += m_serverRelay_OnNewAgentConnected;
m_serverRelay.StartServer(null, 1234);		// Listen every possible network interface

Do not forget to handle OnNewPacketReceived for the newly connected agent, otherwise you will not receive responses from that agent.

void m_serverRelay_OnNewAgentConnected(AgentRelay agentRelay)
{
    agentRelay.OnNewPacketReceived += chatProvider_OnNewPacketReceived;
}

The next step is to enable accepting incoming connections after starting the server:

m_serverRelay.AcceptIncommingConnections = true;

It is possible to send a message to all connected agents at once (Broadcast a message):

BroadcastMessage(int cmdCode, string content, AgentRelay excludedAgent)

The content is optional and you can exclude a specific agent to receive that particular message. Maybe that agent is the caller itself that does not like to stick in a deadly loop over network!

Some Notes

  • ServerRelay constructor

    If you pass true to the constructor which means EnableAutoHandshake, the server will try to handshake with all the connected agents every 15 seconds which is useful to discover faulty agents.

  • OnNewAgentConnected(AgentRelay agentRelay)

    Each time a new agent connects to our server, OnNewAgentConnected will be raised to inform you about the new agent.

  • StopServer()

Do not forget to call this method to clean up all the resources.
If you pass true to this function, it will dispose and cleanup all active agents which were connected to the server.

Points of Interest

While I was developing socket based programs, I faced some situations that the connection were established and it looked very well but in fact because of some unknown reasons, it was faulty in fact!

I came to this point to prepare a handshaking mechanism so the networking layer would be worried about the connection, so simply call a method StartHandshakeAsync and wait for the result.

This method is only supported by AgentRelay class, after calling this method, you have to check the LastHandshakeResult to see the result, ALTHOUGH the ServerRelay will do this automatically and there is no need to call this method manually.

History

  • 7th Apr 015 - First stable release