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


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

是否有一个程序可以立即强制该套接字移出TIME_WAIT状态?


109
2017-09-03 12:57




如果你在这里 “太多 TIME_WAIT 在服务器上“,只是 跳过 前三个答案避免了问题而不是回答问题。 - Pacerier


答案:


/etc/init.d/networking restart

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

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

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

请参阅以下TCP状态图: alt text

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

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

RFC 793 将TIME-OUT设置为最大段寿命的两倍,即2MSL。由于MSL(数据包可以在Internet上漫游的最长时间)设置为2分钟,因此2MSL为4分钟。 由于ACK没有ACK,如果被动发送器没有收到对其FIN的ACK(理论上),则主动关闭器不能做任何事情,只要等待4分钟,如果它正确地遵守TCP / IP协议。 。

实际上,丢失的数据包可能很少见,如果全部发生在局域网内或单个机器内,则非常罕见。

逐字回答问题,如何 强制 在TIME_WAIT关闭一个套接字?,我仍然会坚持原来的答案:

/etc/init.d/networking restart

实际上,我会对它进行编程,以便在WMR提到时使用SO_REUSEADDR选项忽略TIME-WAIT状态。 SO_REUSEADDR究竟做了什么?

这个socket选项告诉内核   即使这个端口很忙(在
  TIME_WAIT状态),继续   无论如何重用它。如果它很忙,但是   与另一个州,你仍然会得到   一个已经在使用中的地址错误。它   如果您的服务器已关闭,则非常有用   下来,然后立即重新启动   套接字仍然活跃在其上   港口。你应该知道,如果   任何意外的数据都可以进入   混淆你的服务器,但同时这   是可能的,这是不可能的。


139
2017-09-03 13:11



很好的答案,但不是他的问题的正确答案。重新启动网络会起作用,但之后会重新启动,所以这可能不对。 - Chris Huang-Leaver
@Chris Huang-Leaver,问题是“你可以运行一个程序来立即强制该套接字移出TIME_WAIT状态吗?”如果重新启动可以被认为是运行程序,那么它也是一个正确的答案。为什么你认为这不对? - Eugene Yokota
WMR有最有用的答案(这是我遇到这种问题时所做的)。重新启动网络太激烈而无法解决问题,并且可能需要更长时间而不是简单地等待超时。他的问题的正确答案是“否”,但是不会让你输入两个字母的答案:-) - Chris Huang-Leaver
哦,好吧,下次有一些进程挂在SIGTERM上我只是粉碎我的电脑而不是修复它。 - Longpoke


我不知道你是否有正在运行的特定程序的源代码,但如果是这样,你可以通过以下方式设置SO_REUSEADDR setsockopt(2) 即使套接字处于TIME_WAIT状态,它允许您绑定在同一本地地址上(除非该套接字正在主动侦听,请参阅 socket(7))。

有关TIME_WAIT状态的更多信息,请参阅 Unix套接字FAQ


50
2017-09-03 13:17



但我没有得到已经绑定的错误。当我再次执行程序时,它会在帖子中监听(123456),我也可以看到系统显示该端口的TIME_WAIT,但我仍然可以连接。为什么? - Jayapal Chandran
即使使用SO_REUSEADDR,仍然可以获得“已在使用的地址”错误。有关详细信息,请参阅 hea-www.harvard.edu/~fine/Tech/addrinuse.html。 - Jingguo Yao
@WMR SO_REUSEADDR 不会“关闭”套接字。它只是让您重用已经打开的那些。所以问题仍然是“如何强行关闭套接字 TIME_WAIT?” - Pacerier


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

/proc/sys/net/ipv4/tcp_tw_recycle

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

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

然而, 这一页 包含有关设置此变量时可能出现的可靠性问题的警告。

还有一个相关文件

/proc/sys/net/ipv4/tcp_tw_reuse

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

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

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


32
2017-09-03 13:24



认为后两个例子应该是s / rw / tw /我编辑,但缺乏足够的代表。
取自内核文档:警告。 tcp_tw_recycle和tcp_tw_reuse都可能导致问题。如果不了解正在使用或由启用参数的节点使用的节点之间的网络拓扑,则不应启用它们。通过知道TCP连接状态的节点(例如防火墙,NAT或负载平衡器)的连接可能会因设置而开始丢帧。当有足够多的连接时,问题将变得可见。
将其设置为 1 适用于未来的连接,但那些已经打开的当前连接呢? - Pacerier


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

更新:另一个解决方案是 切割机 这是一些Linux发行版的存储库。


16
2017-10-30 17:32



谢谢!这个工具很棒!让我不必重新开始长期工作。 - Zanson


另一种选择是使用SO_LINGER选项,超时为0。 这样,当您关闭套接字时强制关闭,发送RST而不是进入FIN / ACK关闭行为。这将避免TIME_WAIT状态,并且可能更适合某些用途。


3
2018-06-10 22:33



它还会丢失仍在传输中的任何出站数据,并可能导致另一端出错。不建议。 - user207421
@EJP早早失败几乎总是正确的。网络不可靠,战斗会减慢速度。崩溃的应用程序不能假设任何数据安全地发布。 - Tobu
实际上,我建议任何一天,当另一个端点是一个有缺陷的嵌入式工业总线网关,通过TCP实现自己的应用层可靠传输,其中所述传输阻止连接关闭,除非它收到RST并因此填满该网关的连接限制。那里。我给了你一个非常具体和非常真实的例子,遗憾的是,需要诉诸这样的黑客。 - andyn
@Tobu网络是不可靠的,但TCP试图成为,并且使更糟糕并不构成任何改善,并让TCP做它的工作并不构成“打架”任何东西。 - user207421


另一种解决方案是使用一些可靠的代理或端口转发软件来侦听端口49200,然后使用不同的端口将连接转发到不太可靠的程序的几个实例之一... HAPROXY让人想起。

顺便提一下,你连接的端口非常高。您可以尝试使用0-1024范围之上的未使用的。您的系统不太可能使用较低的端口号作为临时端口。


2
2017-08-21 20:28





TIME_WAIT是套接字编程客户端服务器体系结构中最常见的问题。 等待几秒钟尝试定期是最好的解决方案。 对于实时应用程序,他们需要服务器必须立即起床 它们有SO_REUSEADDR选项。


0
2017-10-13 19:07