4

This specifically is a question on what is going on in the background communications of NetworkStream consuming raw data over TCP. The TcpClient connection is communicating directly with a hardware device on the network. Every so often, at random times, the NetworkStream appears to hiccup, and can be best described while observing in debug mode. I have a read timeout set on the stream and when everything is working as expected, when stepping over Stream.Read, it will sit there and wait the length of the timeout period for incoming data. When not, only a small portion of the data comes through, the TcpClient still shows as open and connected, but Stream.Read no longer waits for the timeout period for incoming data. It immediately steps over to the next line, no data is received obviously, and no data will ever come through until everything is disposed of and a new connection is reestablished.

The question is, in this specific scenario, what state is the NetworkStream in at this point, what causes it, and why is the TcpClient connection still in a seemingly open and valid state? What is going on in the background? No errors thrown and captured, is the stream silently failing in the background? What is the difference between states of TcpClient and NetworkStream?

private TcpClient Client;  
private NetworkStream Stream;  

Client = new TcpClient();  
var result = Client.BeginConnect(IPAddress, Port, null, null);  
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(2));  
Client.EndConnect(result);  
Stream = Client.GetStream();  

try  
{  
    while (Client.Connected)  
    {  
        bool flag = true;
        StringBuilder sb = new StringBuilder();

        while (!IsCompleteRecord(sb.ToString()) && Client.Connected)
        {
            string response = "";
            byte[] data = new byte[512];

            Stream.ReadTimeout = 60000;

            try
            {
                int recv = Stream.Read(data, 0, data.Length);
                response = Encoding.ASCII.GetString(data, 0, recv);
            }
            catch (Exception ex)
            {

            }

            sb.Append(response);
        }

        string rec = sb.ToString();
        // send off data
        Stream.Flush();
    }
}
catch (Exception ex)
{

}
Nic S.
  • 43
  • 3

2 Answers2

0

You are not properly testing for the peer closing its end of the connection.

From this link : https://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.read%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
This method reads data into the buffer parameter and returns the number of bytes successfully read. If no data is available for reading, the Read method returns 0. The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes.

You are simply doing a stream.read, and not interpreting the fact that you might have received 0 bytes, which means that the peer closed its end of the connection. This is called a half close. It will not send to you anymore. At that point you should also close your end of the socket.

There is an example available here :
https://msdn.microsoft.com/en-us/library/bew39x2a(v=vs.110).aspx

// Read data from the remote device.
int bytesRead = client.EndReceive(ar);

if (bytesRead > 0) {
    // There might be more data, so store the data received so far.
    state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));

    // Get the rest of the data.
    client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
                new AsyncCallback(ReceiveCallback), state);
} else {
    // All the data has arrived; put it in response.
    if (state.sb.Length > 1) {
         response = state.sb.ToString();
    }
    // Signal that all bytes have been received.
    receiveDone.Set(); ---> not that this event is set here
}

and in the main code block it is waiting for receiveDone:

receiveDone.WaitOne();

// Write the response to the console.
Console.WriteLine("Response received : {0}", response);

// Release the socket.
client.Shutdown(SocketShutdown.Both);
client.Close();

Conclusion : check for reception of 0 bytes and close your end of the socket because that is what the other end has done.

A timeout is handled with an exception. You are not really doing anything with a timeout because your catch block is empty. You would just continue trying to receive.

Philip Stuyck
  • 6,843
  • 3
  • 23
  • 36
  • Thanks for the contribution, Phillip! There is a lot of logic I pulled out of the code snip for clarity to highlight where the problem was occurring. – Nic S. Jul 03 '15 at 21:09
  • I suspected exactly what you said, but I should clarify. The closing of the remote host is not supposed to happen; it's unexpected. In this case the remote host sends a portion of the transmission and unexpectedly closes. I'm looking for an identifier that would come over the wire to help identify what state the remote host is in at the time of failure. Is there an additional command or low level packet I could identify at the time of closure, or a specific state of the stream I'm looking for on the .net side? I need to gather as much info as I can to take to the manufacturer. – Nic S. Jul 03 '15 at 21:19
  • I am using wireshark to check what happens on the wire. When a TCP connection closes there is a four way handshake taking place. The peer will initiate it by sending a FIN. Your side of TCP will ack it, and when you close the socket then you send a FIN as well, that the peer will ack. – Philip Stuyck Jul 04 '15 at 05:39
  • A downvote is for answers that are not helpful. Please indicate why you downvote so I can improve the answer if needed which in this particular case is not needed, as I think I make quite some effort to help the poster. – Philip Stuyck Apr 16 '17 at 09:54
0

@Philip has already answered ythe question.
I just want to add that I recommend the use of SysInternals TcpView, which is basically a GUI for netstat and lets you easily check the status of all network connections of your computer.
About the detection of the connection state in your program, see here in SO.

Community
  • 1
  • 1
Tobias Knauss
  • 2,931
  • 1
  • 14
  • 39