如何在 TIME_WAIT 中强​​行关闭套接字?

Reh*_*han 125 networking linux unix tcp socket

我在 linux 上运行一个特定的程序,它有时会崩溃。如果你在那之后快速打开它,它会像第一次那样监听套接字 49201 而不是 49200。netstat 显示 49200 处于 TIME_WAIT 状态。

是否有程序可以运行以立即强制该套接字退出 TIME_WAIT 状态?

Eug*_*ota 161

/etc/init.d/networking restart
Run Code Online (Sandbox Code Playgroud)

让我详细说明一下。传输控制协议 (TCP) 旨在成为两个端点(程序)之间的双向、有序且可靠的数据传输协议。在这种情况下,术语可靠意味着如果数据包在中间丢失,它将重新传输数据包。TCP 通过为从对等方接收到的单个或一系列数据包发回确认 (ACK) 数据包来保证可靠性。

这对于诸如终止请求/响应之类的控制信号也是如此。RFC 793将 TIME-WAIT 状态定义如下:

TIME-WAIT - 表示等待足够的时间以确保远程 TCP 收到其连接终止请求的确认。

请参阅以下 TCP 状态图: 替代文字

TCP是双向通信协议,所以在建立连接时,客户端和服务器之间没有区别。此外,任何一方都可以调用退出,并且双方都需要同意关闭以完全关闭已建立的 TCP 连接。

让我们将第一个调用退出作为主动接近者,而另一个同级则是被动接近者。当主动关闭器发送 FIN 时,状态进入 FIN-WAIT-1。然后它收到发送的 FIN 的 ACK,状态进入 FIN-WAIT-2。一旦它也从被动关闭器接收到 FIN,主动关闭器将向 FIN 发送 ACK,状态进入 TIME-WAIT。如果被动关闭器没有收到到第二个 FIN 的 ACK,它将重传 FIN 数据包。

RFC 793将超时设置为最大段寿命的两倍,或 2MSL。由于 MSL(数据包可以在 Internet 上徘徊的最长时间)设置为 2 分钟,因此 2MSL 为 4 分钟。由于没有对 ACK 的 ACK,如果它正确遵守 TCP/IP 协议,主动接近器只能等待 4 分钟,以防万一被动发送方没有收到对其 FIN 的 ACK(理论上) .

实际上,丢失的数据包可能很少见,如果这一切都发生在 LAN 内或一台机器内,则非常罕见。

要逐字回答问题,如何在 TIME_WAIT 中强行关闭套接字?,我仍将坚持我原来的答案:

/etc/init.d/networking restart
Run Code Online (Sandbox Code Playgroud)

实际上,我会对其进行编程,使其使用 WMR 提到的 SO_REUSEADDR 选项忽略 TIME-WAIT 状态。 SO_REUSEADDR 究竟是做什么的?

这个套接字选项告诉内核,即使这个端口很忙(在
TIME_WAIT 状态),仍然继续并重用它。如果它很忙,但处于其他状态,您仍然会收到地址已在使用错误。如果您的服务器已关闭,然后在其端口上的套接字仍处于活动状态时立即重新启动,这将很有用。您应该知道,如果出现任何意外数据,可能会混淆您的服务器,虽然这是可能的,但不太可能。

  • 很好的答案,但不是他问题的正确答案。重新启动网络会起作用,但是重新启动也会起作用,所以这不可能是正确的。 (10认同)
  • WMR 有最有用的答案(这就是我遇到此类问题时所做的)。重新启动网络太剧烈而无法解决,并且可能比简单地等待超时花费更长的时间。他的问题的正确答案是“否”,但 SO 不会让您输入两个字母的答案:-) (9认同)
  • 哦,好吧,下次某些进程挂在 SIGTERM 上时,我只会粉碎我的计算机而不是修复它。 (6认同)
  • @Chris Huang-Leaver,问题是“您是否可以运行一个程序来立即强制该套接字退出 TIME_WAIT 状态?” 如果可以考虑重新启动以运行程序,那么它也将是一个正确的答案。为什么你认为这不正确? (3认同)

小智 52

我不知道你是否有你正在运行的那个特定程序的源代码,但如果有,你可以设置 SO_REUSEADDR 通过setsockopt(2)它允许你绑定到相同的本地地址,即使套接字处于 TIME_WAIT 状态(除非套接字正在积极侦听,请参阅socket(7))。

有关 TIME_WAIT 状态的更多信息,请参阅Unix 套接字常见问题解答

  • 即使使用 SO_REUSEADDR,仍然可能出现“地址已在使用中”错误。有关详细信息,请参阅 http://hea-www.harvard.edu/~fine/Tech/addrinuse.html。 (3认同)

小智 34

据我所知,除了将更好的信号处理程序写入程序之外,没有办法强行关闭套接字,但是有一个 /proc 文件可以控制超时所需的时间。该文件是

/proc/sys/net/ipv4/tcp_tw_recycle
Run Code Online (Sandbox Code Playgroud)

您可以通过执行以下操作将超时设置为 1 秒:

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 
Run Code Online (Sandbox Code Playgroud)

但是,此页面包含有关设置此变量时可能出现的可靠性问题的警告。

还有一个相关文件

/proc/sys/net/ipv4/tcp_tw_reuse
Run Code Online (Sandbox Code Playgroud)

它控制是否可以重用 TIME_WAIT 套接字(大概没有任何超时)。

顺便说一句,内核文档警告您不要在没有“技术专家的建议/请求”的情况下更改这些值中的任何一个。我不是。

该程序必须已编写为尝试绑定到端口 49200,如果该端口已被使用,则增加 1。因此,如果您可以控制源代码,您可以更改此行为以等待几秒钟,然后在同一端口上重试,而不是递增。


ako*_*nov 19

实际上有一种方法可以终止连接 - killcx。他们声称它可以在任何连接状态下工作(我尚未验证)。不过,您需要知道发生通信的接口,默认情况下它似乎假定为 eth0。

更新:另一个解决方案是刀具,它来自一些 linux 发行版的存储库。


max*_*zig 5

就在这里。您可以使用ss立即销毁所有处于 TIME-WAIT 状态的套接字,如下所示:

ss state time-wait sport = 49200 -K
Run Code Online (Sandbox Code Playgroud)

之后,重新启动侦听器应该会成功,而不是由于Address already in use.

注意:此命令需要超级用户权限。


或者,如果您能够修改程序,则可以在绑定套接字之前简单地设置SO_REUSEADDR ,这样尽管陈旧连接处于 TIME-WAIT 状态,绑定仍然会成功。

  • 事实上,在我看来,这是OP问题的唯一正确答案:可以在事后完成,而不是事先更改各种设置(当然,SO_REUSEADDR应该始终用于服务器,但如果在最初运行时没有完成)。 ..),并真正删除了内核的已知条目。当本地网络处于 TIME_WAIT 状态时,任何告诉数据包必须发送的答案都不会更改本地网络堆栈状态上的任何内容。当然,当提出问题时,使用“ss”删除套接字条目的能力可能不可用。 (2认同)