Search code examples
c#.nettcpclienttcplistener

Keeping TCP connection alive


This is probably a stupid question, but hear me out.

On my remote server, I am running a program with the following code:

private const int BufferSize = 1024;
private static readonly byte[] BytesBeingReceived = new byte[BufferSize];

static void Main(string[] args)
{
    TcpListener tcpListener = new TcpListener(localaddr: IPAddress.Any, port: 8080);
    tcpListener.Start();

    clientSocket = tcpListener.AcceptSocket();
    clientSocket.Receive(BytesBeingReceived); // Receives header first

    ReceiveAndUnzip(GetZippedFolderSizeInBytes()); 
    // GetZippedFolderSizeInBytes() reads the header, which contains information about the folder size

    Console.WriteLine("Press any key to exit the program.");
    Console.ReadLine();
}

As expected, the program exits once it becomes connected to a client socket and receives all the data from the client (so far the client is always my local machine).

However, I would like to keep the program running insofar as the server is available (i.e. not restarting, hibernating, shut down, etc.). After receiving all the data from the client, I would like to keep the TcpListener active and wait for possibly more data. If the client disconnects, the TcpListener should still stay alive and wait for future pending connections.

Any suggestions on how I can do so?

UPDATE:

Here is the implementation of GetZippedFolderSizeInBytes():

private static int GetFileSizeInBytes()
{
    return Convert.ToInt32(Encoding.ASCII.GetString(BytesBeingReceived)
                                         .Replace("\0", Empty)
                                         .Split(new[] { Environment.NewLine, ":" }, StringSplitOptions.RemoveEmptyEntries)[1]);
}

Here is the implementation of the ReceiveAndUnzip(int numBytesExpectedToReceive) method:

private static void ReceiveAndUnzip(int numBytesExpectedToReceive)
{
    int numBytesLeftToReceive = numBytesExpectedToReceive;

    using (MemoryStream zippedFolderStream = new MemoryStream(new byte[numBytesExpectedToReceive]))
    {
        while (numBytesLeftToReceive > 0)
        {
            Array.Clear(BytesBeingReceived, 0, BufferSize);
            int numBytesReceived = clientSocket.Receive(BytesBeingReceived, SocketFlags.Partial);
            zippedFolderStream.Write(
                BytesBeingReceived, 
                0, 
                numBytesLeftToReceive < BufferSize ? numBytesLeftToReceive : BufferSize);
            numBytesLeftToReceive -= numBytesReceived;
        }

        zippedFolderStream.Unzip(afterReadingEachDocument: DoMoreStuff);
    }
}

Solution

  • You have to change your code to accept new clients in a loop. You probably may want to use some asynchronous versions of Receive and Accept and spawn some tasks to handle clients also in a loop if you want your clients connected - this is not really the most optimal way, but it should work.

    Network programming is a tricky one. Here are some tips.


    1. Message defragmentation

    You have no guarantee that a single Send call in the client will result in single Receive on the server. Your message can be split into fragments or if you quickly send a couple of messages they can all be joined together and received with a single Receive call.

    One solution to that problem is to prefix the message with a header first e.g. with 4 byte integer. This integer is your message header and just contains the information about how long is your message (in bytes).

    What you want to do is receive until you get this 4 byte integer header and then receive the rest of the message - you now know how long the message is so you receive until you get whole message. Repeat this process for every new message.

    This is called defragmentation or deframing. There are various ways you can implement that: delimiters, messages prefixed with fixed-size headers, some mix between those two.

    2. Keep-alive / Heartbeat mechanism

    In tcp there are problems with half-open and half-closed connections. In short it just means that you cannot be sure whether the connection is still alive - peer/client is connected if you don't exchange any messages. When peer/client disconnects then your Receive call should return 0 - that means the connection is closed, but the way TCP works that's not always the case and you have to account for that to avoid all kinds of related problems.

    The simplest Keep-Alive mechanism you can implement is just some timeout logic e.g. not receiving any message within 2 minutes means peer/client is disconnected. Or add additional heartbeat message sent every X seconds and timeout the peer/client if it didn't send it within some time range.

    3. Use some well-tested library

    If you are not implementing any custom protocol and don't want to solve all the TCP issues by yourself that are already solved by some smart guys then search for a library.