Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.Net Socket doesn't respond to remote disconnect?

I'm writing a small (C#) client application that sends data using a TCP/IP connection to a remote server. I'm using the standard .Net TcpClient object and want to leave the connection open from the client end as I am regularly submitting data packets to the server. However, it is possible that the server may close the connection, in which case I need to know to re-connect before sending my next packet.

Using Wireshark, I can see (only) the following dialogue when the server terminates the connection:

server >>> FIN, ACK
                ACK <<< client

What I do not see is my client responding with a FIN of its own, to complete the connection shutdown. The result is that my client program only finds out that the connection is down after sending the next data packet.

Is there any way I can set up TcpClient or its underlying Socket so as to complete the disconnect, and provide some feedback so that my client code knows to re-connect before sending the next packet?

Added in response to comment below: My sending code is very simple - the object that maintains the TcpClient and NetworkStream member variables, has a member function containing (essentially) the following:

bool sent = false;
byte[] buffer = Encoding.UTF8.GetBytes(dataString);
while (!sent)
{
    try
    {
        m_outStream.Write(buffer, 0, buffer.Length);
        sent = true;
    }
    catch (Exception ex)
    {
        if (m_outStream != null) { m_outStream.Dispose(); }
        m_client = new TcpClient(AddressFamily.InterNetwork);
        m_client.Connect(ipAddress, ipPort);
        m_outStream = m_client.GetStream();
    }
}

With m_client and m_outStream initialized, this simply performs a single pass every time. Then using Wireshark I can see the server send a packet with flags FIN, ACK to which the client responds with ACK.

The next time I call my function, the data is sent out with PSH, ACK, and the server responds with RST, ACK but does not read the incoming data. No exception is raised by the client.

Then I call my function a second time, and an exception is raised causing the connection to be re-started.

like image 847
Nick Avatar asked Jan 17 '23 05:01

Nick


2 Answers

In general you should be able to use the Connected property on the TcpCient instance:

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

However:

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. Your code should assume the socket is connected, and gracefully handle failed transmissions.

Try the following to make sure the Connected flag holds the most recent state:

var tcpClient = new TcpClient ();
tcpClient.Connect();

var stream = tcpClient.GetStream();

// buffer size need to be > 0
int[] buffer = new int[1];
stream.Read(buffer, 0, 0);

if(!tcpClient.Connected)
    // do something

Based on decompilation it should be possible to read 0 bytes from a stream, at least there is no check in the .NET Framework TcpClient that prevents this. However it might not be aloud in the external code that is called from the framework to actually read from the network stream.

Be sure to Dispose of both the TcpClient and the Stream after your done, disposing the TcpClientdoes not dispose of the Stream so you need todo this manually, afterwards all resources are freed up (after GC).

like image 160
ntziolis Avatar answered Jan 19 '23 20:01

ntziolis


From MSDN TcpClient.Connected property:
Type: System.Boolean
true if the Client socket was connected to a remote resource as of the most recent operation; otherwise, false.

This means, you would have to send some data to the server to detect the broken connection. Reading does not work, as you read from the buffer.

See my answer on a related question (https://stackoverflow.com/a/25680975/2505186),
linking the answer of someone else, where a suitable way is described to detect the connection status: How to check if TcpClient Connection is closed?

Important for you:
The client does not close the connection automatically, when the server does so. The connection is in CLOSE_WAIT state then at the client side and in FIN_WAIT2 state at the server side. See the related section in the wikipedia article Transmission Control Protocol. Using the code from the linked answer above, you can detect that the connection is about to get closed. Further, you can finish the closing procedure then and reopen it if needed.

like image 28
Tobias Knauss Avatar answered Jan 19 '23 21:01

Tobias Knauss