TCP套接字/NetworkStream意外失败

4

这个问题涉及到使用TCP消费原始数据时,NetworkStream在后台通信中发生了什么。TcpClient连接直接与网络上的硬件设备通信。不时地,在随机时间,NetworkStream似乎会出现故障,并且在调试模式下进行观察时最能描述它。我在流上设置了读取超时时间,当一切按预期工作时,当步过Stream.Read时,它将坐在那里等待传入数据的超时时间长度。当没有这样的情况发生时,只有一小部分数据通过,TcpClient仍然显示为打开和连接状态,但是Stream.Read不再等待传入数据的超时时间。它立即跳到下一行,显然没有接收到数据,并且在一切都被处理并重新建立新连接之前,不会有任何数据通过。

问题是,在这种特定情况下,NetworkStream处于什么状态,是什么导致了它,为什么TcpClient连接仍然处于看似打开和有效的状态?后台发生了什么?没有抛出或捕获错误,流是否在后台默默失败?TcpClient和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)
{

}
2个回答

0

@Philip已经回答了这个问题。
我只想补充一点,我推荐使用SysInternals TcpView,它基本上是netstat的GUI,可以让您轻松检查计算机的所有网络连接状态。
关于在您的程序中检测连接状态,请参见这里在SO


太好了,谢谢。这两个肯定会对我有帮助的! - Nic S.

0

您没有正确地测试对等端关闭连接的情况。

来自此链接:https://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.read%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
此方法将数据读入缓冲区参数并返回成功读取的字节数。如果没有可供读取的数据,则Read方法返回0。Read操作读取尽可能多的数据,最多达到size参数指定的字节数。如果远程主机关闭连接,并且已接收到所有可用数据,则Read方法立即完成并返回零字节。

您只是进行了一次流读取,并没有解释您可能已经收到了0字节,这意味着对等端关闭了连接。这称为半关闭。它不会再向您发送任何内容。在那时,您也应该关闭套接字的端口。

这里有一个示例可用:
https://msdn.microsoft.com/zh-cn/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
}

在主代码块中,它正在等待接收完成:

receiveDone.WaitOne();

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

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

结论:检查是否接收到0字节并关闭套接字的一端,因为另一端已经这样做了。
超时是通过异常处理的。您实际上没有对超时做任何事情,因为您的catch块为空。您只会继续尝试接收。

感谢你的贡献,Phillip!我从代码片段中提取了很多逻辑以增强清晰度,并突出问题发生的位置。 - Nic S.
我怀疑你所说的正是我想到的,但我需要澄清一下。远程主机关闭不应该发生;这是意外的。在这种情况下,远程主机发送了部分传输并意外关闭。我正在寻找一种标识符,它将通过电线传输以帮助确定远程主机在故障时处于什么状态。在关闭时是否有其他命令或低级数据包,或者我是否需要查找 .net 方面的特定流状态?我需要收集尽可能多的信息,以便向制造商提供。 - Nic S.
我正在使用Wireshark来检查网络上发生的情况。当TCP连接关闭时,会进行四次握手。对等方将通过发送FIN来启动它。您的TCP端将确认它,并在您关闭套接字时也发送一个FIN,对等方将确认它。 - Philip Stuyck
一个 downvote 是给那些没有帮助的回答的。请说明你为什么要 downvote,这样我就可以改进答案,如果需要的话,在这种特殊情况下不需要,因为我认为我已经很努力地帮助了发帖者。 - Philip Stuyck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接