Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Server not recognizing disconnect when TCP client is forcibly closed

Introduction

A single instance of my client application will make two outgoing TCP connections to the remote server - a main connection and a file transfer connection.

When the client application is forcibly closed - sometimes the server does not acknowledge that both of the socket connections have been dropped.

The server will either detect that both connections have been dropped or that only the main connection has been dropped which is undesirable.

Understand that the problem occurs on only one of several test machines and that the file transfer connection is actively transmitting data when the client is forcibly closed.

Having analyzed the network traffic - I learned that the OS was in fact acknowledging two RST flags! Because of this I am inclined to believe that the problem lies with the server code.

Code

Using the Socket.BeginReceive method, my callback which is predominantly responsible for detecting disconnections looks like this:

private void ReadCallback(IAsyncResult asyncResult) 
{
    try 
    {
        int bytesTransferred = _clientSocket.EndReceive(asyncResult);
        _logger.Debug(GetHashCode() + " Received " + bytesTransferred + " bytes from " + RemoteEndPoint);

        if (bytesTransferred > 0) 
        {
            _clientSocket.BeginReceive(_readMessageBuffer, ReadCallback);
            _logger.Debug(GetHashCode() + " Issued asynchronous read for " + RemoteEndPoint);
        } 
        else 
        {
            _logger.Debug(GetHashCode() + " Client disconnected. Received zero bytes. ");
        }
    } 
    catch (SocketException socketException) 
    {
        _logger.DebugException(GetHashCode() + " Client disconnected. SocketException thrown", socketException);
    }
    catch (ObjectDisposedException objectDisposedException)
    {
        _logger.DebugException(GetHashCode() + " Client disconnected. ObjectDisposedException thrown", objectDisposedException);
    }
    catch (Exception exception) 
    {
        _logger.DebugException(GetHashCode() + " Client disconnected. Exception thrown", exception);
    }
}

In order to isolate the problem I have stripped the code back so that the server only issues an asynchronous read each time a portion of data is read from the network.

Could somebody please share some insight or speculation as to why on certain machines the server application will not always detect both socket disconnections?


This is the unmodified log produced when the server application fails to acknowledges that both underlying socket connections have been dropped.

This is the unmodified log produced when the server application successfully acknowledges that both underlying socket connections have been dropped.

like image 932
Caster Troy Avatar asked Oct 11 '13 10:10

Caster Troy


People also ask

How is TCP disconnection detected?

In TCP there is only one way to detect an orderly disconnect, and that is by getting zero as a return value from read()/recv()/recvXXX() when reading. There is also only one reliable way to detect a broken connection: by writing to it.

How to close connection in golang?

Close() works. According to the Go Documentation, by default, after the net. Conn. Close() method is executed; in the background, the Operating System will finish sending any data and then close the TCP session.


2 Answers

The first thing I see is that there is no exception handler around the Socket.EndReceive call. That call can indeed throw exceptions.

You are using the version that is supposed to return a SocketError, but nothing in the docs says that it does that in all cases.

Because ReadCallback is called by the system you won't have any exception handlers further up the chain that could even catch and print an error when an exception is thrown by EndReceive. Thus it will appear that nothing has really gone wrong but the socket is left half open.

I suggest adding the exception handler around Socket.EndReceive and printing a message or setting a breakpoint there when it happens. I think that will point to the problem. You probably just need to dispose/close the Socket when you get an exception from EndReceive.

Hope that helps - Harold

like image 60
huntharo Avatar answered Oct 19 '22 03:10

huntharo


The thing about computer networking is that messages travel across a network. There is no psychic link between them. If one endpoint just disappears, if the network breaks or the computer loses power, there is no psionic signal sent off to the other end saying "I am no longer able to talk to you".

So the only way to reliably find out if the computer you're talking to is still there, is to try to talk to it. When the network fails to deliver packets to the computer, then you find out. But when a computer goes from "saying nothing because it has nothing to say", to "saying nothing because it isn't there", all your end is seeing is just that it's saying nothing. You can't tell the difference.

like image 27
jalf Avatar answered Oct 19 '22 03:10

jalf