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

标签 c# .net sockets tcp network-programming

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

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

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

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

networkStream.Write(new byte[0], 0, 0);

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

int bytesRead = networkStream.Read(buffer, 0, 4);
if (bytesRead == 0)
    break;

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

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

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

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

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

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

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

最佳答案

来自 MSDN 的 NetworkStream.Read Method (Byte[], Int32, Int32) :

The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter. If no data is available for reading, the Read method returns 0.


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

networkStream.Write(new byte[0], 0, 0);

鉴于,在阅读数据时,您声称

  1. "My client code does expect data from the Server from time to time, so it has a thread that blocks on networkStream."
int bytesRead = networkStream.Read(buffer, 0, 4);
if (bytesRead == 0)
   break;

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

  1. "...just wanted to know if .NET's Socket can distinguish between an empty byte array and connection closing."

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

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

while(networkStream.DataAvailable) { // your code logic}

关于C# NetworkStream - 在读取时区分关闭的套接字和 0 字节数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46082011/

相关文章:

c# - 我该怎么做 float :right in Windows Form Application?

c# - 我需要担心阻塞任务吗?

.net - 是否可以使用 Gmail API (.NET) 获取 Gmail 联系人列表?

.net - 被 VS2012 吞咽异常的噩梦难倒

c# - 如何使用 nunit 测试异步方法

c# - 如何根据父 div 中的符号删除它?

c# - 当我的鼠标指向 C# 中的按钮时,我想禁用其他按钮

c - udp socket编程文件传输

java - PHP 卡在 socket_read 上

mysql - Docker 无法通过 socket '/var/run/mysqld/mysqld.sock' 连接到本地 MySQL 服务器 (2) Ubuntu