【发布时间】:2019-02-01 01:01:21
【问题描述】:
考虑以下顺序:
- 客户端应用程序(网络浏览器)打开多个 TCP 连接到不同的网络服务器;
- 然后以太网电缆断开;
- 然后关闭客户端应用程序;
- 以太网电缆断开连接数小时;
- 以太网电缆已重新连接;
- 我看到来自长期关闭的客户端应用程序连接到的一些服务器的“TCP keep-alive”数据包(每 60 秒,持续数小时)!
通常,当应用程序关闭时,应用程序会启动关闭每个打开的套接字,然后 TCP 层会尝试向每个远程端点发送一个 FIN 数据包。如果物理上可以发送 FIN 数据包,并且这种发送确实发生了,则本地端点从 ESTABLISHED 状态进入 FINWAIT_1 状态(并等待从远程端点接收 ACK 等)。但是,如果物理链接断开,则 TCP 本地端点无法发送该 FIN,并且服务器仍然假定 TCP 连接仍然存在(并且客户端对“关闭”函数的调用将无限期阻塞,直到物理假设套接字设置为阻塞模式,链接已重新建立,对吗?)。
在任何情况下,在所有传统联网应用程序(例如,网络浏览器)长时间关闭一段时间后重新连接以太网电缆时,我会以精确的 60 秒间隔从三个独立的网络服务器接收“TCP Keep-Alive”数据包几小时!
Wireshark 显示这些TCP Keep-Alive 数据包发送到的本地端口号,但TCPView 和netstat -abno 均不显示任何应用程序正在使用的本地端口号。使用 Process Explorer 查看每个正在运行的进程的“TCP/IP”属性也不会显示任何匹配的端口号。由于任何正在进行的子进程(例如插件应用程序),我认为端口不会因为僵尸“进程记录”(例如,Web 浏览器进程)而被保留,但我不确定我的观察是否使用 TCPView/netstat/Process Explorer 足以排除这种可能性。
鉴于远程 Web 服务器(例如 Akamai 服务器)的身份,我相信这些连接是通过“最近”使用 Web 浏览器建立的。但是,即使浏览器已关闭,物理链接已中断数小时,这些保持活动仍然来自这三个 Web 服务器。
如果连接出现在TCPView,我可以简单地选择它们并手动关闭它们。但是,客户端 TCP 端点似乎早已不复存在。
与此同时,我很困惑为什么服务器要重试这么多次才能得到对其保持活动数据包的回复。
TCP 保持连接行为通常由三个参数控制:\
(1) 等待下一次“爆发”或“探测”尝试的时间;
(2) 在一次“探测”尝试期间发送每个保持活动数据包之间的时间间隔;
(3) 在“突发”被视为失败之前“探测”尝试的最大次数(因此 TCP 连接被视为永久断开)。
对于我从三个不同服务器看到的 TCP 保持活动数据包,“探测”重试之间的时间间隔正好是 60 秒。但是,“探测”重试的最大次数似乎是无限的,这对于任何服务器来说似乎都是一个非常糟糕的选择!
虽然我很好奇这种无情的保持活动流是如何创建和维持的,但我更感兴趣的是如何使用客户端应用程序来强制服务器端端点关闭,因为有'不存在接收这些保持活动数据包的现有本地 TCP 端点。
我的粗略想法是创建一个应用程序,它创建一个 TCP 模式套接字,绑定(允许端口号重用)到传入的 keep-alives 指向的端口号,然后调用“open”,然后“关闭”,希望服务器端点使 TCP 状态转换以一种或另一种方式达到关闭状态!另一种方法可能是创建一个原始模式套接字,并接收 TCP keep-alive 数据包(这只是一个 ACK),然后形成并发送一个适当的 FIN 数据包(具有适当的序列号等,以获取长期终止的客户端应用程序显然已停止),然后在发送最终 ACK 之前接收 ACK 和 FIN。
最后一点——我知道会有翻白眼和嘲笑:这里的工作环境是在 Windows 7 上的 VirtualBox 中运行的 Windows XP SP3!因此,我更喜欢可以在 Windows XP SP3 中实现目标(关闭半开 TCP 连接)的代码或开源应用程序。当然,我可以重新启动快照,这可能会关闭连接——但我更感兴趣的是学习如何获取有关网络连接状态的更多信息,以及我可以做些什么来处理这种情况TCP 状态问题。
【问题讨论】:
-
我相信默认情况下 TCP 甚至不发送任何保持活动数据包。应用程序必须首先启用此功能。你确定这些是保活数据包吗?
-
@usr 是的,Wireshark 清楚地为我收到的定期 ACK 数据包指示“TCP keep-alive”。而且,是的,它需要服务器显式设置套接字选项以启用 TCP 保持活动行为。
标签: sockets networking tcp