4

I've been working on a socket client program in C# and am wondering how to detect when the other end of a socket has disconnected itself "ungracefully" as in a network cable being unplugged or a hard reset.

I have these functions below to access the socket and according to the SO question here and this MSDN article, the best way to check for a disconnected socket is to send a 1-byte message with a length of 0. If an exception is thrown and WSAEWOULDBLOCK is not the error code then the socket is disconnected. I have tried this but after hard reseting the server connection the client will call Send(new byte[1], 0, 0, SocketFlags.None) and return successfully and the Receive() command right afterwards returns the WSAEWOULDBLOCK error.

What gives??

Here's my code below. _socket is set to non-blocking mode:

private int nonBlockRecv(byte[] recvBytes, int offset, int size, SocketFlags sf)
{
    int bytesRecv = 0;

    while (true)
    {
        try
        {
            nonBlockSend(new byte[1], 0, 0, sf);
            bytesRecv = _socket.Receive(recvBytes, offset, size, sf);
            break;
        }
        catch (SocketException excp)
        {
            if (excp.ErrorCode != 10035) // WSAEWOULDBLOCK
                throw excp;
        }
    }

    return bytesRecv;
}

private int nonBlockSend(byte[] sendBytes, int offset, int size, SocketFlags sf)
{
    int bytesSent = 0;

    while (true)
    {
        try
        {
            _socket.Send(sendBytes, offset, size, sf);
            break;
        }
        catch (SocketException excp)
        {
            if (excp.ErrorCode != 10035) // WSAEWOULDBLOCK
                throw excp;
        }
    }

    return bytesSent;
}

Edit: This may be beneficial but the server is Windows Mobile device. I read in another thread that different OSs maybe able to send socket close signals when they're dying. Perhaps the Windows Mobile OS does not do this??

Community
  • 1
  • 1
Zac
  • 2,325
  • 3
  • 23
  • 33
  • How can a 1-byte message have a length of zero? :-) – Ondrej Tucny Apr 15 '11 at 17:53
  • Great question haha. That's what the MSDN article says to do. However, if I do set the size to 1 then it works! I guess that's pretty obvious but then that extra bit is sent through the stream to the server-side socket. I would like for Receive() to throw an exception other than the would block one if the socket is closed but that doesn't seem to be the case... – Zac Apr 15 '11 at 18:04

2 Answers2

6

If the remote computer gracefully disconnects the session, the Socket.Receive() method will return with 0 bytes. You must detect that to know that the remote end has disconnected:

int recv = sock.Receive(data);
if (recv == 0)
{
    // Remote client has disconnected.
}
else
{
    // Remote client has sent data.
}

Also, even if there SocketException arises you can identify the exception for socket disconnection.

Hope this helps solve your problem.

Ankit
  • 2,753
  • 1
  • 19
  • 26
  • But what about when the session is closed in a non-graceful manner as in a network cable being unplugged? The Receive() call doesn't throw an exception or return, it just continues to block. – Zac Apr 15 '11 at 18:12
  • I've edited my post but the server-side is a WM6 device. When the server closes the program I get notified on the client program that the socket connection is closed but not when I hard reset the device. – Zac Apr 15 '11 at 18:37
  • It looks like there's no direct way to tell from the receiving end of a WM6 socket if the connection has been cancelled. Your method works in most other cases though, so thank you! – Zac Apr 18 '11 at 22:25
  • 1
    What does graceful mean in this case? I.e. the remote computer calling `serverSocket.disconnect()`? – user2635088 May 23 '16 at 07:43
1

I know this is late but I came up with a cunning solution for this.

I had to communicate with 3rd party software which expected a carriage return on every command sent, otherwise it ignored it.

During the main phase my client socket was in a loop receiving responses from the 3rd party software. My solution isn't ideal but the basic premise is that I put a receive timeout on the socket so that the loop will try to read for 5 seconds then fall into the catch, then loop again. Before each receive I call my own isconnected method which performs a small write without a carriage return, so it's ignored by the 3rd party software yet will give me a reliable fallover if the network has dropped. All I do if the write fails is throw a LostConnectionException and handle that externally.

If you are writing both server and client, you can quite easily come up with some checkdigit that the other ignores.

This may not be perfect but it's reliable for me.

while (numberOfBytesRead == 0)
{          
    try
    {
        IsConnected();
        _socket.ReceiveTimeout = 5000;
        numberOfBytesRead = _socket.Receive(myReadBuffer, 0, myReadBuffer.Length, SocketFlags.None);

    }
    catch (Exception e)
    {
        if (e.GetType() == typeof (LostConnection))
        {
            Status = SocketStatus.offline;
            throw;
        }
    }
}

and the isconnected method would look something like this

public bool IsConnected(Socket s)
{
    try
    {
        ASCIIEncoding encoder = new ASCIIEncoding();
        byte[] buffer = encoder.GetBytes("test");
        s.Send(buffer, 0, buffer.Length, SocketFlags.None);
    }
    catch (Exception)
    {
        throw new LostConnection();
    }
    return s.Connected;
}
Rob
  • 43
  • 6