你有没有遇到过这种情况:
服务器程序崩溃重启,或者你手动kill掉进程再重启,结果:
bind: Address already in use
明明进程都已经死了,端口为什么还"占着"?
用netstat一看:
tcp 0 0 0.0.0.0:8080 0.0.0.0:* TIME_WAIT
这个TIME_WAIT是什么?为什么要等那么久?能不能直接干掉它?
这篇文章把这些问题一次性讲清楚。
一、TIME_WAIT 是什么?它从哪里来?
回忆上一篇讲的四次挥手,主动关闭连接的一方在发完最后一个 ACK 之后,不会立刻消失,而是进入一个叫TIME_WAIT的等待状态。先从四次挥手中定位 TIME_WAIT 的位置,让读者立刻对号入座。
发完最后一个 ACK,主动关闭方没有立刻消失,而是在TIME_WAIT里"站岗",等待2MSL时长。
MSL(Maximum Segment Lifetime)是一个 TCP 报文在网络中的最大存活时间,Linux 默认 60 秒,所以 2MSL =120 秒。
这 120 秒,就是端口被"占住"的根源。
二、为什么非得等 2MSL?两个缺一不可的理由
很多人觉得这 2MSL 是"多此一举"。其实不等不行,有两个硬逻辑:
1. 理由一:保证最后一个 ACK 能送到
发出去的 ACK 可能在网络里丢失。一旦丢失,被动关闭方(服务端)会重发 FIN。
如果主动关闭方已经 CLOSED 了,收到这个重传的 FIN,只能回一个 RST(连接重置)。被动关闭方会认为连接异常关闭——这不是我们想要的优雅断开。
等 2MSL,就是为了"我如果没收到重传的 FIN,说明我的 ACK 对方收到了,可以安全关闭了"。
2. 理由二:让网络里的"迷路包"彻底消亡
网络里可能还漂着上一条连接残留的数据包——它们走了弯路,迟迟没到。
如果立刻用同一个{源IP:源端口, 目标IP:目标端口}四元组建立新连接,这些旧包被新连接收到,数据就乱了。
等 2MSL,就是等网络中所有残留报文(最多漂 1 MSL)都超时失效,新连接绝对不会被旧包污染。
三、TIME_WAIT 堆积的真实场景
理解了原理,来看看生产中它是怎么出问题的。
1. 场景一:服务端主动关闭连接(最常见)
HTTP/1.0 时代,每次请求后服务端都会主动close(),自己就成了主动关闭方。高并发下大量短连接被关闭,TIME_WAIT快速堆积:
# 查看 TIME_WAIT 数量
$ ss -tan | grep TIME-WAIT | wc -l
18342 # 一万多个!端口耗尽只是时间问题
2. 场景二:服务进程崩溃重启
进程崩溃,内核自动关闭所有 socket,服务器成了主动关闭方,占用的端口全进入TIME_WAIT。重启一试:
$ ./server
bind: Address already in use # 端口还被 TIME_WAIT 占着
用ss确认一下:


闽公网安备 35020602001684号