【问题标题】:Howto detect that a network cable has been unplugged in a TCP connection?如何检测 TCP 连接中的网线是否已拔出?
【发布时间】:2012-12-23 00:22:24
【问题描述】:

我有一个 C++ 网络应用程序,它接受来自客户端的 TCP 连接,然后在套接字上等待,直到客户端决定发送数据(有时它们很长时间都不会发送任何东西,这没关系)。

它主要在客户端崩溃或机器关闭时检测错误情况,但是当连接到客户端的网络电缆被拔下时需要几分钟才能注意到,我希望它尽快注意到这种情况。

我无法控制客户端,也无法让他们发送类似“ping”的信息。我的服务器确实向客户端发送了一个“ping”数据包(但它们不会发送响应),但即使拔下电缆,write() 也会返回正确的字节数(我看到 TCP 堆栈发送重试Wireshark 中的数据包)。

发现连接丢失的最佳方法是什么?如果我可以在 write() 调用中检测到它,那将是最方便的。

我需要它在 Windows 和 Linux 上工作。

【问题讨论】:

    标签: c++ sockets tcp network-programming


    【解决方案1】:

    抱歉,如果没有 ping/keepalive,就无法及时检测到异常断开连接。甚至操作系统也不总是知道电缆已被拉动。这就是write() 仍然有效的原因——套接字很高兴地在其输出缓冲区中缓冲数据,等待稍后发送,因为套接字状态尚未被操作系统无效。最终,socket会在内部超时,此时OS终于可以使连接失效,让socket在后续操作中报错。但正如您所注意到的,这可能需要很长时间。

    由于您无法发送应用层 ping,请至少尝试启用套接字层 keep-alives。这可能会有所帮助。仅在 Windows 2000+ 上,您可以通过WSAIoctl() 使用SIO_KEEPALIVE_VALS 套接字选项,它允许您设置保持活动的实际计时器值。在所有平台上,您都可以通过setsockopt() 使用SO_KEEPALIVE 选项,但这不允许您配置计时器值,因此使用默认值。

    【讨论】:

    • 我查看了 SO_KEEPALIVE,但 Stevens 说它每 2 小时发送一次 keepalive。我正在寻找更快的东西。
    • 这就是微软在 Windows 上引入SIO_KEEPALIVE_VALS 的原因,因此可以根据每个连接自定义保活间隔。并非所有平台都提供该功能。 Solaris 有一个TCP_KEEPALIVE_THRESHOLD 选项。一些平台支持TCP_KEEPINTVLTCP_KEEPCNT。如果您不能使用应用层 ping 或套接字层 keepalive,那么您就是 SOL,抱歉。您可以做的最好的事情是使用特定于平台的硬件 API 来获取电缆状态,但是在与电缆完全无关的插座上可能会出现很多问题,因此这并不是一个真正可行的解决方案。跨度>
    【解决方案2】:

    你的问题很复杂。您和您的客户之间有很多事情可能会出错。不仅仅是“拔掉”的电缆。

    如果您只是想知道您的用户是否仍然在线,您可以建立一个新的 TCP 连接。因为您需要完成 3 次握手才能成功建立 TCP 连接,所以您知道连接成功初始化时客户端在线。这样做的问题是,如果您想保持当前连接处于活动状态,则需要另一个端口。不知道这是否是您的问题。

    但听上去,您并没有真正从客户端发送和接收数据(除了一些 ping 数据)。因此,您可以简单地将您的应用程序设置在一个循环中以每隔 X 秒设置一个 TCP 连接(前两个步骤 - 因此接收 ACK - 应该足以确定您的客户端是否仍在处理网络数据)。如果您在 X 毫秒内没有得到响应,您可以非常可靠地说您的客户端或介于两者之间的某物停止“工作”。

    希望这会有所帮助。如果没有,请提供有关您的工具在做什么的更多信息。

    【讨论】:

    • 我根本无法控制客户端(遵循不会更改的固定协议的封闭源代码产品)。我只能控制已建立的 TCP 连接的结束。
    • 在这种情况下,只要您有可以连接的可靠服务,上述方法就可以正常工作。如果不是这种情况,您可以随时尝试枚举一些常用服务。你得到一个TCP连接?客户起来了,你没有。尝试不同的服务。所有服务都返回 false?客户端宕机了。或类似的方式。
    • 我无法主动连接到客户端。他们不监听任何端口,可能在防火墙后面等。我所拥有的只是客户端与服务器建立的连接。
    • 嗯...我现在看到你的问题了哈哈。你确实处于一个有点困难的境地。不要以为我能提供更多帮助。对不起,伙计,希望你能找到解决办法。
    【解决方案3】:

    很遗憾,无法区分从另一端拉出的电缆与任何其他丢包原因。话虽如此,您可以将另一端的连接丢失近似为在足够长的时间段(例如 T)内发生的“无限丢包”。 TCP 跟踪数据包丢失,因此执行此操作的一般方法是:

    • 获取连接中未确认的字节数(比如B)
    • 发送数据,size = N
    • 设置超时 = T,当它触发时,再次检查未确认的字节数。如果是B+N,则假设对方已经失去连接。此时,您可以尝试 ICMP echo 来验证您的假设。

    获取连接的 TCP 特定信息不是 UNIX 上的标准接口,也绝对不能移植到 Windows。在 Linux 上,有一个名为 TCP_INFO 的套接字选项,您可以通过 getsockopt() 调用它。谷歌应该给你一些例子。我不知道 Windows 上是否有等效选项。

    另一种方法(即近似跟踪连接丢失)是通过 RAW 套接字。打开一个 RAW 套接字并对其进行过滤以仅接收用于您的连接的 TCP 流量。然后,与其从 TCP 获取信息以确定您是否从另一端得到任何东西,不如等待从另一端接收 any 数据包。如果在规定的时间内得到了东西,那就说明对端还在。

    【讨论】:

    • getsockopt(TCP_INFO) 可能会提供足够的信息来在 Linux 上解决这个问题。 Windows 上是否有类似的调用来检索 TCP 统计信息?
    • 看起来 Winsock 没有等效的套接字选项。我没有看得太难 - 如果您搜索,请注意连接上的任何传入字节数(不一定是未确认的字节)应该有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-03
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多