TCP 四次挥手的过程是什么?为什么不能三次挥手?
TCP 四次挥手是断开连接的过程:主动方发 FIN,被动方回 ACK,被动方再发 FIN,主动方回 ACK。之所以要四次而不是三次,是因为 TCP 全双工——每个方向必须单独关闭。第二次挥手时被动方可能还有数据没发完,不能把 FIN 和 ACK 合并,只能先确认,等数据发完再关自己的方向。
追问
TIME_WAIT 为什么要等 2MSL?
两个原因。第一,确保最后的 ACK 能到——如果 ACK 丢了,被动方会重传 FIN,TIME_WAIT 状态的主动方可以重新回 ACK。如果主动方直接 CLOSED,收到重传的 FIN 只能回 RST,被动方会报连接异常。第二,让网络中属于这个连接的旧报文段全部消亡——MSL 是报文最大生存时间(RFC 793 建议 2 分钟,Linux 实际是 60 秒),等 2MSL 确保来回两个方向的旧包都过期,不会干扰后续新连接。
TIME_WAIT 过多怎么办?
短连接场景下(比如 HTTP/1.0 每个请求一个连接),主动关闭方会积累大量 TIME_WAIT,每个占一个五元组(源IP、源端口、目的IP、目的端口、协议),端口耗尽后新连接无法建立。解决方法:启用 tcp_tw_reuse(允许 TIME_WAIT 端口给新连接用,依赖时间戳判断旧包)、改用长连接(HTTP/1.1 Keep-Alive)、让客户端主动关闭(服务端被动关闭不产生 TIME_WAIT)。注意 tcp_tw_recycle 在 NAT 环境下会导致连接失败,Linux 4.12 后已移除此选项。
为什么不能三次挥手?
假设第二次和第三次合并——被动方收到 FIN 后立刻发 FIN+ACK。问题是被动方可能还有数据没发完:它收到主动方的 FIN 只是说"我不发了",不代表"你也不能发了"。被动方需要继续发送剩余数据,发完才能关自己的方向。如果提前发 FIN,这些数据就丢了。只有在被动方恰好也没有数据要发时,才能合并成三次挥手——这就是延迟 ACK 场景下偶尔观察到三次挥手的原因。
CLOSE_WAIT 很多是什么问题?
CLOSE_WAIT 是被动方收到 FIN 后、还没发 FIN 之前的状态。大量 CLOSE_WAIT 说明应用层没有调用 close()——通常是因为代码 bug:忘了关 socket,或者线程池满了来不及处理。一个典型的坑是:HTTP 服务端在请求处理异常时没关连接,客户端超时断开后,服务端停留在 CLOSE_WAIT。排查方法:netstat 统计 CLOSE_WAIT 数量,配合 lsof 找到对应进程,检查代码里的 socket 关闭逻辑。
写段代码
python# 查看系统 TCP 连接状态分布 import subprocess result = subprocess.run(['netstat', '-tan'], capture_output=True, text=True) states = {} for line in result.stdout.splitlines()[2:]: parts = line.split() if len(parts) >= 6: state = parts[5] states[state] = states.get(state, 0) + 1 for state, count in sorted(states.items(), key=lambda x: -x[1]): print(f"{state:20s} {count}")