【问题标题】:Checking a TcpClient is actually connected检查 TcpClient 是否实际连接
【发布时间】:2021-01-02 06:25:44
【问题描述】:

和许多人一样,我一直在研究测试 TCP 会话是否处于活动/活跃的主题。有太多半有效的解决方案似乎是一个不必要的难题。一个连接在测试自己之前什么都不知道。然后尝试发送可能会成功,尽管连接实际上已丢失。轮询似乎会为连接提供误报。某些服务器配置为不响应 ping。唯一真正的测试似乎是尝试建立新的连接并感知尝试是否成功。这似乎是不必要的笨拙,但协议没有一种轻量级的方式来回答“在这个特定的时刻,是否可以将数据从客户端传输到服务器并验证它是否被接收”的问题,这似乎有点疯狂?'

我正在使用 .net 框架和其中公开的 TCP 对象。断开网络电缆时,肯定会立即向所有消费者发出连接丢失的信号。然而,情况并非如此,我对连接的任何感觉都没有意识到这种损失。只有尝试重新建立连接才会发现物理链路已断开。

我错过了什么?

【问题讨论】:

  • 它必须有多轻?我见过的最可靠的方法是通过良好的请求和响应。你必须发送数据,一旦服务器发回一些东西,你就知道你可以走了。即使有长期存在的 Websocket 连接,也会有一个不断发生的 ping/pong 过程。
  • 您可以尝试回答的问题是“在很短的时间内,是否可以将数据从客户端传输到服务器” - 这是网络、独立机器等的本质。您永远无法回答提前你的下一步行动是否能够成功。
  • 而 ping 告诉你“这台机器响应回显请求”,不是“这台机器能够处理我的下一个请求”
  • 所有优点,沟通会随着时间的推移而发生,除了已经发生的事情之外,没有任何确定性。事实上,连接似乎很难测试,我认为这是协议的基本部分。当我按下 F5 时,Chrome 不知何故知道有线连接已丢失,但 TCPClient 对象却不能。
  • @arjabbar 这意味着客户端和服务器在数据级别有一个约定的 ping 和响应,这也意味着开发人员可以控制两端。这一切都是为了确定协议本身肯定负责的事实?

标签: .net tcp intermittent


【解决方案1】:

TCP 并没有像您认为的那样真正发挥作用,尽管我们可以做一些事情来使其更好地为您工作。但首先让我们更好地了解它是如何工作的,以及为什么您会看到自己的行为。

当您打开 TCP 连接时,TCP 使用 3 次握手来建立连接。客户端发送一个SYN,服务器响应SYN+ACK,然后客户端发回一个ACK。如果双方都没有尝试发送任何内容,则连接将闲置在那里。您可以从机器上拔下电缆。一棵树可能会倒下并破坏您的互联网服务。互联网提供商可以来维修您的互联网服务,您可以将电缆插回以太网端口。然后客户端可以写入套接字,它应该被传递到服务器。 (不幸的是,防火墙故意破坏标准,而您的防火墙可能已决定在您等待 ISP 修复您的服务时使连接超时。)但是,如果您在连接电缆时尝试建立另一个连接被拔掉,TCP 会尝试发送一个 SYN,并且很可能会发现“没有到主机的路由”。所以它无法建立新的连接。

如果您在 Internet 服务中断时尝试写入套接字,TCP 将尝试发送数据并等待来自服务器的 ACK。在重传超时后,如果它没有收到 ACK,它将再次尝试并在超时时以指数方式回退。通常在 15 次尝试后它会放弃,这通常需要半小时到一个半小时。

如您所见,TCP 试图在面对故障时保持弹性,而您希望快速了解故障。需要对连接故障做出快速反应的系统(例如,通常会在连接故障时取消未结订单的电子证券交易所)通过定期发送心跳消息并在心跳充分过期时采取行动来处理此问题,作为更高级别协议的一部分。

但如果您无法控制协议,您可以使用一些套接字选项来改善这种情况。 SO_KEEPALIVE 导致 TCP 定期发送 keepalive 数据包,最终会超时,具体取决于 TCP_KEEPIDLE、TCP_KEEPINTVL 和 TCP_KEEPCNT 的设置。 TCP_USER_TIMEOUT 允许您设置写入套接字的数据可以保持未确认的超时时间。

这两个选项的具体工作方式和交互方式取决于实现,您必须考虑在没有未确认的数据、有未确认的数据以及消费者缓慢导致零窗口时会发生什么.一般来说,建议将它们与 TCP_USER_TIMEOUT 设置为 (TCP_KEEPIDLE + TCP_KEEPINTVL*TCP_KEEPCNT) * 1000 一起使用以获得一致的结果。

我们的朋友 Cloudflair 有一个很好的Blog entry,关于它们究竟是如何协同工作的,但不幸的是在 Linux 上。对于 Windows,我不知道有什么比这更全面的。

【讨论】:

  • 老实说,我进入这个项目时并不知道 TCP 是如何工作的,尽管过去花了一些时间来了解网络。理想情况下,我可以 ping 我尝试连接的服务器,如果失败,就知道连接已经坏了。服务器拒绝 ping,所以我查看了 TCP 层,因为我无法控制数据协议。令我惊讶的是,对于 TCP 的所有巧妙复杂性,一个简单的“进展如何”,可能是重复握手的形式,是不可用的。不过感谢您的出色回答,Windows 似乎对这些设置进行了硬编码。
  • @JCollins 我很确定你可以在 Windows 上设置这些。要保持活动,请查看 SocketOptionName Enum 文档中的套接字选项 KeepAlive、TcpKeepAliveInterval、TcpKeepAliveRetryCount 和 TcpKeepAliveTime:docs.microsoft.com/en-us/dotnet/api/…。对于用户超时,以下声明它有效,但枚举不在标题中:stackoverflow.com/a/12948084/7218127
  • 我阅读的文件表明其中一些参数是全局的,在注册表中设置。留在 .net 框架围墙花园中,我看不到如何设置重试计数,这是注册表中的关键之一。用户超时看起来不错,但正如您所说,它不在枚举中,这意味着可能会进入非托管状态。目前我正在研究一种解决方法。
  • 默认有全局设置,但在套接字上设置套接字选项只会影响该套接字。另外,我不确定我是否相信说用户超时的人工作,即使它没有在标题中定义,因为他 #defines 选项与 linux 使用的值相同,如果它以某种方式将是一个奇怪的巧合碰巧在windows上是一样的。
猜你喜欢
  • 1970-01-01
  • 2010-11-26
  • 1970-01-01
  • 2019-12-04
  • 2022-12-01
  • 2016-10-13
  • 1970-01-01
  • 2016-10-24
相关资源
最近更新 更多