0

I am trying to have a server allow TCP connections and and echo out any newline delimited messages being sent. I want multiple clients to be able to connect one after another, maintaining the same server socket. Here's my code:

TcpClient client;

while (true) {
    Console.Write("Waiting for connection... ");
    client = listener.AcceptTcpListener();
    nStream = client.GetStream();
    sReader = new StreamReader(nStream);
    Console.WriteLine("Connected!");

    while (client.Connected) {
        string line = sReader.ReadLine();
        Console.WriteLine(line);
    }
    Console.WriteLine("#Client Disconnected")
}

Unfortunately, when the remote client disconnects, it never escapes the "while (client.Connected)" loop. Instead I get an infinite write to STDOUT.

Doct0rZ
  • 153
  • 1
  • 2
  • 7
  • Its a long time since i used these classes, but i don't think you can use the property like this, i think the connected property only works when its the connection on the client, not the client on the server (difficult to explain). I guess you will need to implement some kind of ping or alive check to make this work properly, at least that was what i did the last time i worked that low level in c#. If its for learning go ahead, but otherwise i give you the advice to use a third party lib like [lidgren](http://code.google.com/p/lidgren-network-gen3/) – dowhilefor Dec 09 '12 at 04:07

1 Answers1

3

Basically, the property that you're using TcpClient.Connection does not do what you think it does. From the MSDN documentation:

http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.connected.aspx

Because the Connected property only reflects the state of the connection as of the most recent operation, you should attempt to send or receive a message to determine the current state. After the message send fails, this property no longer returns true. Note that this behavior is by design. You cannot reliably test the state of the connection because, in the time between the test and a send/receive, the connection could have been lost.

The gist is that the property TcpClient.Connection was not updated after the host disconnected but before your server blocked waiting to read another line from the stream. You need a more reliable way to detect if the connection is active before you block.

Turns out, this question has been asked before. So, I borrowed the answer from here and adapted it to the format that you're using in the OP.

https://stackoverflow.com/a/8631090

    static void Main(string[] args)
    {
        TcpClient client = new TcpClient();
        TcpListener listener = new TcpListener(IPAddress.Loopback, 60123);
        listener.Start();

        while (true)
        {
            Console.WriteLine("Waiting for connection...");
            client = listener.AcceptTcpClient();
            Console.WriteLine("Connection found");
            StreamReader reader = new StreamReader(client.GetStream());

            string line = string.Empty;
            while (TestConnection(client))
            {
                line = reader.ReadLine();
                Console.WriteLine(line);
            }
            Console.WriteLine("Disconnected");
        }
    }

    private static bool TestConnection(TcpClient client)
    {
        bool sConnected = true;

        if (client.Client.Poll(0, SelectMode.SelectRead))
        {
            if (!client.Connected) sConnected = false;
            else
            {
                byte[] b = new byte[1];
                try
                {
                    if (client.Client.Receive(b, SocketFlags.Peek) == 0)
                    {
                        // Client disconnected
                        sConnected = false;
                    }
                }
                catch { sConnected = false; }
            }
        }
        return sConnected;
    }

This works for me when I test it, and the reason that it works is that you cannot tell if the connection is closed until you attempt to read or write from it. You can do that by blindly trying to read/write and then handling the IO exceptions that come when the socket is closed, or you can do what this tester method is doing and peek to see if the connection is closed.

Hope this helps you

EDIT:

It should be noted that this may or may not be the most efficient way to check if the connection is closed, but it is purely to illustrate that you must check the connection yourself on the server side by reading/writing instead of relying on TcpClient.Connection.

EDIT 2:

My sample doesn't clean up old resources very well, apologies to anyone who had an OCD reaction.

Community
  • 1
  • 1
Rob Ocel
  • 326
  • 1
  • 8
  • Just in case anyone else sees this - an easy way to check if the TcpClient is disconnected after hooking it up to a StreamReader is to check if StreamReader.ReadLine() returns null. – Gareth Oakley Jan 21 '14 at 13:47