Mars Mars - 4 days ago 4
C# Question

Detecting unexpected socket disconnect

This is not a question about how to do this, but a question about whether it's wrong what I'm doing. I've read that it's not possible to detect if a socket is closed unexpectedly (like killing the server/client process, pulling the network cable) while waiting for data (BeginReceive), without use of timers or regular sent messages, etc. But for quite a while I've been using the following setup to do this, and so far it has always worked perfectly.

public void OnReceive(IAsyncResult result)
{
try
{
var bytesReceived = this.Socket.EndReceive(result);

if (bytesReceived <= 0)
{
// normal disconnect
return;
}

// ...

this.Socket.BeginReceive...;
}
catch // SocketException
{
// abnormal disconnect
}
}


Now, since I've read it's not easily possible, I'm wondering if there's something wrong with my method. Is there? Or is there a difference between killing processes and pulling cables and similar?

Jon Jon
Answer

It's perfectly possible and OK to do this. The general idea is:

If EndReceive returns anything other than zero, you have incoming data to process.

If EndReceive returns zero, the remote host has closed its end of the connection. That means it can still receive data you send if it's programmed to do so, but cannot send any more of its own under any circumstances. Usually when this happens you will also close your end the connection thus completing an orderly shutdown, but that's not mandatory.

If EndReceive throws, there has been an abnormal termination of the connection (process killed, network cable cut, power lost, etc).

A couple of points you have to pay attention to:

  1. EndReceive can never return less than zero (the test in your code is misleading).
  2. If it throws it can throw other types of exception in addition to SocketException.
  3. If it returns zero you must be careful to stop calling BeginReceive; otherwise you will begin an infinite and meaningless ping-pong game between BeginReceive and EndReceive (it will show in your CPU usage). Your code already does this, so no need to change anything.
Comments