C# NetworkStream - 在读取时区分关闭的套接字和 0 字节数组

Mar*_*ica 5 .net c# sockets network-programming tcp

[已解决,问题基于不正确的假设]

在使用 TCP 时,我遇到了 NetworkStream.Read 在两种不同情况下返回值 0 的问题,我无法区分这两种情况。

一些背景知识 - 我有一个使用长度前缀消息通过 TCP 通信的有效客户端-服务器解决方案。然而,由于大部分通信(除了一些初始消息交换)发生在客户端到服务器上,服务器没有一个很好的方法来知道客户端是否仍然连接。找出这个问题的一种方法是不时向客户发送一些东西,这就是我决定做的。

我知道我可以在我的协议中添加一个专用的“ping”消息,并在客户端中简单地忽略它,但我也在测试其他可能性。我尝试过的一件事是向客户端发送一个空字节数组,如下所示:

networkStream.Write(new byte[0], 0, 0);
Run Code Online (Sandbox Code Playgroud)

一切看起来都不错,它似乎正在发送一个没有数据的 TCP 数据包……但是!我的客户端代码不时需要来自服务器的数据,所以它有一个线程阻塞 networkStream.Read,如下所示:

int bytesRead = networkStream.Read(buffer, 0, 4);
if (bytesRead == 0)
    break;
Run Code Online (Sandbox Code Playgroud)

根据文档,如果另一端关闭连接,则 Socket.Read(或 NetworkStream.Read)返回 0。这是真的,但就我而言,在发送空字节数组后,Read(...) 也返回 0。

到目前为止,我无法区分这两种情况。在两种情况下(连接关闭和空字节数组),读取后检查的 Socket.Connected 为。有没有其他方法可以处理这个问题?

同样,我确实知道发送这个空数组几乎与为此目的添加新类型的消息相同。我不是在这里寻求解决方案……只是想知道 .NET 的 Socket 是否可以区分空字节数组和连接关闭。

编辑:我很抱歉打扰大家一个问题,这最终是基于不正确的假设。我的测试不是在我的生产代码上完成的,而且太草率了。这导致我得出错误的结论。基本上,我正在测试的是,如果我在一端执行 Write(new byte[0]...),另一端的 Read(...) 将返回 0。确实如此,但不是由于发送。我用来测试的 TcpClient 超出了范围,这(我假设)导致它被 GC 处理,因此连接关闭,导致 Read 返回 0。我确实在 TcpClient 没有被处理的情况下重复了测试/丢失了,无论我发送多少个空字节数组,Read 都不返回任何内容。

起初,我预计 Nagle 的算法会把事情搞砸,但在这种情况下并没有 - 1 字节数组没有延迟地到达,因为我在本地主机上进行测试。我可能会使用套接字进行不同的测试,并明确禁用 Nagle 的算法,但我认为这不会改变任何事情。

现在我只需要检查发送这样的数组是否真的能让我检测到断开连接,但那是另一回事,不在这个问题的范围内。

编辑 2:我对此做了一些更多的测试,并发现,尽管有一些建议,例如在这里(这似乎是一个有效的信息来源),执行空发送并不能识别断开的连接。我物理上断开了网络电缆,我的服务器每 5 秒执行一次空发送。就这样持续了几分钟,没有检测到断开连接。如果我决定发送任何数据(即使是单个字节),最多 20 秒后就会检测到断开连接。

Am_*_*ful 3

来自 MSDN\'s NetworkStream.Read 方法 (Byte[],\xe2\x80\x82Int32,\xe2\x80\x82Int32)

\n\n
\n

读取操作读取尽可能多的可用数据,最多可达大小参数指定的字节数。如果没有数据可供读取,则 Read 方法返回 0。

\n
\n\n
\n\n

发送数据时,您将发送一个空字节数组并使用以下方式写入零字节:

\n\n
networkStream.Write(new byte[0], 0, 0);\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,在阅读数据时,您声称

\n\n
\n
    \n
  1. “我的客户端代码确实需要时不时地从服务器获取数据,因此\n它有一个线程阻塞在networkStream上。”
  2. \n
\n
\n\n
int bytesRead = networkStream.Read(buffer, 0, 4);\nif (bytesRead == 0)\n   break;\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是,您再次尝试将 4 个字节读取到字节数组中,这显然会继续等待。那么,你还期待什么呢!

\n\n
\n
    \n
  1. “...只是想知道 .NET 的 Socket 是否可以区分空字节数组和连接关闭。”
  2. \n
\n
\n\n

连接关闭完全是另一回事,它涉及关闭 Socket 连接之前的几个步骤。所以,显然,它与发送或接收零字节不同!

\n\n

-> 最后,正如@WithMetta在评论中所暗示的,请检查数据是否可以使用NetworkStream.DataAvailable Property.

\n\n
while(networkStream.DataAvailable) { // your code logic}\n
Run Code Online (Sandbox Code Playgroud)\n