views:

691

answers:

4

I'm developing a server in C# which can accept only one client and I need to know when this client is disconnected to be able to accept another connection requests.

I'm using a first Socket which continuously listen to connection request with Socket.BeginAccept and accept or reject clients. When a client is accepted, the new Socket which is returned by Socket.EndAccept is used for the communication between client and server. Then, the server waits for commands from the client with Socket.Begin/EndReceive and sends responses. The server uses a Telnet-like protocol, which means that each command and each line of response must end with \r\n.

To detect if the client has been disconnected, I've installed a timer which sends every 500ms an empty message ("\r\n") to the client. If the client is disconnected, an exception is thrown by the Socket. This exception is caught by the server which closes the current session and accepts new connection. This solution is robust but implies unneeded traffic over the network and must be handled correctly by the client which must filter dummy messages before getting an actual response.

I've tried to send an empty buffer (Socket.Send(new byte[1], 0, 0)), but it seems that doesn't work in the direction server->client.

Another solution might be to handle the case where Socket.EndReceive returns 0 byte. It works fine for the case of a disconnection which occurs during "idle" time. But if the client disconnects during a message transfert, the server doesn't always see it and waits indefinitely.

I've already seen several threads and questions about this problem, but I've never seen any good solution.

So my question is: what is the best way to detect a disconnection in .Net ?

+1  A: 

The only other option is if it is TCP is to have TCP send a keep-alive every so often, which is still polling such as what you're doing now but handled at the TCP layer so you're protocol doesn't need to know.

There's no way around the polling however since without sending something to the other client and getting a response you have no way to know whether it's still connected or not.

Keep alive may also be required anyway when communicating through stateful packet inspection such as standard NAPT to avoid having the remote server drop the session due to in-activity.

KeeperOfTheSoul
OK, I've added code which enables KeepAlive for my socket. But what am I suppose to expect now ? Nothing happens when the client breaks the connection... I expected to receive an Exception somewhere... Should I read or write something periodically ?
cedrou
I though you were using Begin/EndReceive? If so, the BeginReceive should run the callback and when you call EndReceive to get the result an exception should be raised, that or an end of stream 0 byte read.
KeeperOfTheSoul
OK, I get an exception now... Thanks
cedrou
A: 

You can use the below method to find if a client is still connected. This

public static bool IsConnected(this TcpClient client)
{
    try
    {
        bool connected = !(client.Client.Poll(1, SelectMode.SelectRead) && client.Client.Available == 0);

        return connected;
    }
    catch
    {
        return false;
    }
}

This answer is for testing socket, which is how I got my code snippet.

David Basarab
This method works fine if the client has explicitly closed the connection, but not if the connection has been broken (cable unplugged for instance).
cedrou
Which is where keep alive comes in, the OS and TCP protocol will handle periodic messages to the client to see if it is alive, and polling the socket will check the local status. If a keep alive fails the socket should report that its no longer connected.
KeeperOfTheSoul
Though when using async Begin/EndReceive the polling shouldn't be required as you should receive a call back with the error when the socket fails a keep alive.
KeeperOfTheSoul
A: 

Do you have control over the client? If so, cant you just have the client send a special packet to the server saying that it is disconnecting, and then disconnect the socket?

Are you trying to detect the case that the client has actually disconnected (using either Socket.Shutdown(), or Socket.Close()) or whether the client is idle for a huge amount of time, and therefore needs to be ejected?

In either case, have the client send a periodic message (just as you have done) to the server. The server can keep track of the last heartbeat, and if the client misses more than 3 heartbeats, you can disconnect the client. yes, it involves extra data, but that is not too bad in the grand scheme of things. If you tune it so that it sends the heartbeat at a good enough period, so that you get the advantage of knowing if the client is alive, and do not go too long between heartbeats, you could have a very good system.

feroze