Click here to Skip to main content
15,883,825 members
Articles / Programming Languages / C#

C# TCP Server

Rate me:
Please Sign up or sign in to vote.
4.96/5 (43 votes)
8 Apr 2013CPOL6 min read 240.7K   29.4K   91   35
A light weight easy to use .NET TCP server library

Introduction

This article describes a library that provides an easy to use a TCP server component for the .NET environment that does not have the limitations that many .NET TCP server tutorials have.

Background

For one project I worked on the client had a requirement for multiple TCP server interfaces (4 in all), when I googled how to do a TCP Server in C# I found that there wasn't an easy to use TCP server component (like there is in the Delphi/C++ Builder environment) for the .NET framework (TcpListener class does half the job, but there was nothing to manage the connected clients).

None of the C# TCP server tutorials I found were written with the idea of code sharing between multiple TCP servers projects. I also found that all the tutorials had various limitations (some would only support only one connected client, others would create a new thread for each connection meaning the thread limit is the connection limit), so i decided to write my own without these limitations.

Using the code

The source contains 2 projects, TcpServer, and TestApp (TestApp is provided as an example, you can otherwise ignore it). Add the TcpServer project into your solution, after compiling you should find the TcpServer component in the toolbox ready to be placed onto a form, service, component or other container. Alternatively you could manually add the component to the toolbox and use as a component or manually add the project reference, then and call the constructor directly.

To start the TCP server you'll have to call a sequence of code like this;

C#
TcpServer tcpServer1 = new TcpServer(); //in constructor (auto added if added as a component)
 
private void openTcpPort(int port)
{ 
    tcpServer1.Port = port;
    tcpServer1.Open();
}
 
private void closeTcpPort()
{
    tcpServer1.Close();
}  

Calling Open() will open the specified TCP port for listening and start the 2 internal threads that handle the main functions of the TCP server. Conversely Close() stops the 2 internal threads and closes the port, releasing all system resources.

The Port property is the TCP port opened or to be opened, note that changing Port while the port is open could cause some undesired consequences. The property IsOpen will tell you if the port is open before changing it.

There are 3 callback events OnConnect, OnDataAvailable and OnError.

OnConnect is called when a new client connects, OnDataAvailable is called when a previously connected client has something in its buffer ready to read.

Both OnConnect and OnDataAvailable will pass a TcpServerConnection class (which is essentially a slim rap-around for the TcpClient class) that represents the client that connected/has data available.

Both events are called from a thread specifically created for them, neither event will be called again (for that client) while this thread is still active (i.e. when you have multiple callbacks at once it will be for different clients). If there is any data still in the buffer after returning from one of these callbacks the OnDataAvailable event will be almost immediately called again.

Example callback 1;

C#
public delegate void invokeDelegate();
private void tcpServer1_OnDataAvailable(tcpServer.TcpServerConnection connection)
{
    byte[] data = readStream(connection.Socket);

    if (data != null)
    {
        string dataStr = Encoding.ASCII.GetString(data);

        invokeDelegate del = () =>
        {
            handleInput(dataStr);
        };
        Invoke(del);

        data = null;
    }
}

protected byte[] readStream(TcpClient client)
{
    NetworkStream stream = client.GetStream();
    while (stream.DataAvailable)
    {
        //call stream.Read(), read until end of packet/stream/other termination indicator
        //return data read as byte array
    }
    return null;
}

Example callback 2;

C#
private void tcpServer1_OnDataAvailable(tcpServer.TcpServerConnection connection)
{
    byte[] data = readStream(connection.Socket);

    if (data != null)
    {
        string dataStr = Encoding.ASCII.GetString(data);

        string reply;

        lock(someKeyObject)
        {
            reply = getReply(dataStr);
        }
        connection.sendData(reply);

        data = null;
    }
}
As you can from this example the TcpServerConnection has a property called Socket, this is a TcpClient class, so you can use it in the same you would if you were writing a TCP client program (minus the, often messy, reconnection logic).

Also illustrated above, the TcpServerConnection has a sendData() function, this will take a string, which will be converted using a given System.Text.Encoding encoding (ASCII by default), which is queued for one of the TcpServer's internal threads to send down the stream. You can, of course, send data directly using the TcpClient's functions, though if you do it is recommended that you do not use either of the send functions in this library, otherwise it could lead to packets being sent out of order.

The TcpServer's OnError callback is called directly from one of the 2 threads that operate the TCP Server. It is unlikely you will see this event called except in extreme cases (e.g. you've run out of memory). Calls to this event are triggered by unhandled errors, the Exception causing this is passed as a parameter to the event.

The other properties of TcpServer are as follows;

MaxSendAttempts; when calling the Send() function or the TcpServerConnection's sendData() function one of the TcpServer's internal threads will process the actual sending, if an IOException occurs while attempting to send it will retry up to MaxSendAttempts times before discarding the message.

Connections; (read only) this is a list of TcpServerConnections representing all the connected clients. Calling this property will give you a copy of the internal list.

IdleTime; when either of the internal threads detects there is no work to do they will wait for up to this many milliseconds before check again. This value should always be low, there should be no reason to increase it above the default, if you have high traffic then this should be set very low.

MaxCallbackThreads; this represents the maximum number of callback threads to allow at any one time. What this should be depends on how many threads your hardware and version of Windows can handle, and how many other things are running on the server. The default value of 100 is conservative, if you have high traffic you should increase it.

VerifyConnectionInterval; this is how often (in milliseconds) the TcpServer should verify that a client is still connected. The verifying process can take some time so if you have high traffic this should be set high, however problems arise when a client drops out and tries to reconnect before the TcpServer has verified the previous connection (usually leading to the new connection dropping out). I.e. this value is indicative of how long a client that has to wait before they can successfully reconnect. So you should set this to the minimum value you can get away with.

Encoding; Every TcpServerConnection has a property of the same name, which is the System.Text.Encoding used to encode data passed through sendData() or Send(). The property on the TcpServer represents the default for all new connections, by directly changing this on the TcpServer it will change all clients that have the default encoding to the new econding. To change the default without changing the clients use the SetEncoding() function with the changeAllClients parameter set to false. Encoding is Encoding.ASCII by delault.

As noted above, the TcpServer also has a Send() function. It will send the message passed to it to all connected clients (the closest thing to a broadcast you can do in TCP). Calling this is the same as calling the sendData() function on every item in Connections. The string passed will be converted to a byte array according to the Encoding property of each client.

History

2013-04-08 - Released to CodeProject.

2014-03-11 - Fixed calling close now always correctly clears the connections list (Thanks Bart - Member 10655793).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionsend receved data packets to local ports Pin
Member 147464125-Mar-20 21:33
Member 147464125-Mar-20 21:33 
GeneralMy vote of 5 Pin
Member 147464124-Mar-20 3:41
Member 147464124-Mar-20 3:41 
QuestionCallbacks not working Pin
MohsenDianati21-Apr-19 22:00
MohsenDianati21-Apr-19 22:00 
AnswerRe: Callbacks not working Pin
Member 358375818-Aug-21 13:24
Member 358375818-Aug-21 13:24 
QuestionHow reply from server to concox GT06N gps device after receiving login packet Pin
Nitin Surya23-Nov-17 20:50
Nitin Surya23-Nov-17 20:50 
AnswerRe: How reply from server to concox GT06N gps device after receiving login packet Pin
Usman Asif27-Apr-19 6:47
Usman Asif27-Apr-19 6:47 
QuestionCant Connect two clients Pin
sajith wijerathne23-Jun-17 19:09
sajith wijerathne23-Jun-17 19:09 
Questionvery userfull... Pin
Member 1179245825-Apr-16 4:20
Member 1179245825-Apr-16 4:20 
Questionerror Pin
mohsen nassaji14-Mar-16 4:30
mohsen nassaji14-Mar-16 4:30 
QuestionWhy too slow Pin
hiok200020-Nov-15 2:05
hiok200020-Nov-15 2:05 
QuestionIPv6 support? Pin
Member 1056870616-Oct-15 8:39
Member 1056870616-Oct-15 8:39 
GeneralMy vote of 5 Pin
I-Flite7-Oct-15 3:24
I-Flite7-Oct-15 3:24 
Questionconsole mode Pin
EaglePC6-Apr-15 3:21
EaglePC6-Apr-15 3:21 
BugReceiving bigger data by TCP Pin
RRM763-Mar-15 4:52
RRM763-Mar-15 4:52 
GeneralRe: Receiving bigger data by TCP Pin
Mohammad Ahmad19-Jan-16 6:50
Mohammad Ahmad19-Jan-16 6:50 
QuestionSetting IP for Server Pin
Zygot7-Nov-14 9:46
Zygot7-Nov-14 9:46 
QuestionSending to 1 endpoint Pin
Member 1095138816-Jul-14 10:38
Member 1095138816-Jul-14 10:38 
AnswerRe: Sending to 1 endpoint Pin
Member 1095138821-Sep-14 10:31
Member 1095138821-Sep-14 10:31 
GeneralRe: Sending to 1 endpoint Pin
Member 1001814611-Sep-15 4:55
Member 1001814611-Sep-15 4:55 
QuestionSending to 1 connected endpoint Pin
Member 1095138816-Jul-14 9:49
Member 1095138816-Jul-14 9:49 
General5 from me. Pin
Brian Oh2-Jul-14 2:48
Brian Oh2-Jul-14 2:48 
QuestionGet Listening IP Pin
MatyaspokoCZ24-Mar-14 10:36
MatyaspokoCZ24-Mar-14 10:36 
Questionminor issue Pin
Member 106557939-Mar-14 7:48
Member 106557939-Mar-14 7:48 
QuestionApp hangs on Increasing Number of clients Pin
nitin-aem1-Mar-14 0:01
nitin-aem1-Mar-14 0:01 
Questionvery nice :) Pin
Member 1059323412-Feb-14 21:56
Member 1059323412-Feb-14 21:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.