TcpClient/NetworkStream 没有检测到断开连接

7
我用 TcpClientNetworkStream 创建了一个简单的持久套接字连接,用于我们的游戏。连接、发送消息和正常断开连接(退出应用程序/关闭服务器等)都没有问题。
然而,在某些情况下,客户端无法检测到与服务器的断开连接。我测试的最简单方法是拔掉 Wi-Fi 路由器上的网络电缆或将手机设置为飞行模式,但在本该是稳定的 Wi-Fi 上的游戏中也发生过这种情况。
通过查看 NetworkStream 等文档,它说检测断开连接的唯一方法是尝试向套接字写入内容。很好,但是当我尝试时,写操作会像没有错误一样通过。我可以像这样写入多个消息,一切似乎都很好。只有在插回电缆后,它才会发现已经断开连接(所有消息都被缓冲?)。 TcpClient 设置为 NoDelay,并且每次写入后都调用 Flush()
我尝试过:
  • NetworkStream 写入消息-无效
  • CanWriteConnected 等均返回 true
  • TcpClient.Client.Poll( 1000, SelectMode.SelectWrite ); - 返回 true
  • TcpClient.Client.Poll( 1000, SelectMode.SelectRead ) && TcpClient.Client.Available == 0 - 返回 true
  • TcpClient.Client.Receive(buffer, SocketFlags.Peek) == 0 - 当连接时,阻塞约 10-20 秒,然后返回 true。当没有服务器时,永远阻塞(?)
  • NetworkStream.Write() - 不会抛出错误
  • NetworkStream.BeginWrite() - 不会抛出错误(即使调用 EndWrite()
  • 设置 WriteTimeout - 没有效果
  • 有一个特定的时间,我们没有从服务器收到消息(通常有一个保持活动状态的消息)- 我曾经这样做过,但是将其删除了,因为由于延迟等原因,我们得到了很多误报(一些客户端会看到 10-20 秒的延迟)
我在这里做错了什么吗?有没有办法让 NetworkStream 在向应该已断开的套接字写入内容时抛出错误(就像它应该做的那样)?
我对保持活动状态没有问题(默认情况下,服务器将通知客户端它已经很久没有收到任何消息,而客户端将发送一个心跳信号),但是目前,根据 NetworkStream 的说法,一切都很好。

这是一个关于游戏的问题,因此最好的情况是检测速度足够快(因为用户在游戏中可以移动,直到需要进行服务器调用,其中一些会阻塞用户界面,导致游戏似乎已经崩溃)。

我正在使用Unity,因此这是 .Net 2.0。

1个回答

3

拔掉wifi路由器的网线,这是一个很好的测试。如果你这样做,远程方是不会被通知的。它怎么可能发现呢?它无法发现。

当我尝试时,写入操作像没有问题一样通过了

写入操作可以(而且实际上已经)被缓存。它们最终进入一个黑洞……没有回复返回。唯一检测到这种情况的方法是使用超时。

那么我在这里做错了什么吗?

你尝试了很多事情,但从根本上讲,如果没有回复告诉你断开连接,你就无法发现它。使用超时。


谢谢 - 拔掉网络电缆是我唯一可以测试的方法 - 实际的错误发生在生产代码中,而用户应该连接到稳定的WiFi。而且,通过超时,你的意思是客户端如果在X时间内没有收到消息,对吧? - divillysausages
"稳定的WiFi" 我从未见过这样的事情 :) 不过没关系,这个问题可能由许多原因引起。你必须能够处理它;关于超时:必须在想要检测连接丢失的一方进行设置。似乎很明显...我理解你的问题了吗? - usr
好的,我想我明白了 - 写入操作正在被缓冲,所以对于客户端来说,一切都很好。而且,是的,关于超时 - 我只是习惯于人们谈论服务器端超时,所以我想澄清一下。 - divillysausages
在我接受答案之前的最后一个问题:是否有一种方法可以设置套接字的最大“缓冲”时间。即,如果它已经被缓冲了X秒而没有被发送,那么就断开连接?这是否仅由自定义客户端超时来处理? - divillysausages
Nagling是导致发送主机上大多数缓冲的原因。也许您可以更改nagling时间(默认值:200ms)。这并没有太大帮助,因为数据已经成功发送了。只是没有接收到。TCP堆栈在这里无能为力。无论堆栈实现超时还是您的代码实现超时,原则上都不会改变任何事情。TCP堆栈唯一能做的就是执行重传,但这并不能拯救您。 - usr

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