简体   繁体   中英

TCP Socket/NetworkStream Unexpectedly Failing

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)
{

}

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 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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM