TIME_WAIT端口耗尽表现为服务端拒绝新连接、日志报Cannot assign requested address、ss -s中tw计数超2~3万;根本原因是短连接密集导致四元组耗尽本地端口池,且服务端主动close()引发TIME_WAIT堆积。

TIME_WAIT 端口耗尽的典型错误现象
服务端突然拒绝新连接,日志里反复出现类似 Cannot assign requested address 或 Address already in use,netstat -ant | grep :<port> | wc -l</port> 显示成千上万个 TIME_WAIT 状态连接堆积在本地端口(尤其是客户端主动关闭、服务端作为连接接收方时);ss -s 输出中 tw 计数远超 2~3 万。
这不是应用层逻辑错误,而是内核网络栈资源被占满——每个 TIME_WAIT 占用一个四元组(src_ip:src_port:dst_ip:dst_port),而服务端通常复用固定 dst_port(如 8080),只能靠客户端 IP + 端口组合来区分连接。当并发短连接密集(比如 HTTP/1.1 频繁建连、健康检查探针太激进),TIME_WAIT 就会卡在本地端口池里出不来。
确认是否真是 TIME_WAIT 导致端口耗尽
别急着调内核参数,先做三件事验证归因:
- 运行
ss -tan state time-wait sport = :<your_server_port> | wc -l</your_server_port>—— 只统计本服务端口上的TIME_WAIT连接数(替换<your_server_port></your_server_port>为实际监听端口),若 > 28000,基本可锁定 - 查可用端口范围:
cat /proc/sys/net/ipv4/ip_local_port_range,典型是32768 60999(共约 28232 个),和上面数字对得上就更可信 - 看是否真缺“临时端口”:
cat /proc/net/nf_conntrack | grep "dport=<your_server_port>" | grep "TIME_WAIT" | wc -l</your_server_port>(需启用 conntrack)或直接观察netstat -s | grep -i "failed to allocate"是否有failed to allocate ephemeral port记录
服务端代码里避免主动关闭连接(关键!)
C++ 服务端(如基于 epoll + socket 自研或用 libevent/boost.asio)如果在收到请求后调用了 close() 或 shutdown(fd, SHUT_WR) 主动终结连接,就会让服务端进入 FIN_WAIT_2 → TIME_WAIT,把端口锁死 2MSL(Linux 默认 60 秒)。这是最常被忽略的根源。
正确做法是:让客户端发起关闭(HTTP/1.1 的 Connection: close 场景下,客户端发 FIN,服务端响应 ACK + FIN,进入 CLOSE_WAIT → LAST_ACK,不占 TIME_WAIT);服务端只负责响应完立刻 close() 对端 socket,自己不发 FIN。
立即学习“C++免费学习笔记(深入)”;
- 用
setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger))强制立即断开?不行——这会让连接跳过TIME_WAIT,但可能丢数据,且违反 TCP 协议,线上禁用 - 用
SO_REUSEADDR?它只允许bind()时重用处于TIME_WAIT的本地地址,不能减少数量,只是让服务重启不失败 - 真正有效的:在业务逻辑中,除非明确需要服务端终止(如长连接心跳超时),否则不要对已建立的连接调用
close();让 client 控制生命周期
内核参数调整要谨慎,仅作兜底
调整 /proc/sys/net/ipv4/tcp_fin_timeout(默认 60)能缩短 TIME_WAIT 存活时间,但改小到 30 以下有风险:NAT 后客户端重传 FIN 可能被丢,导致连接无法彻底清理;tcp_tw_reuse(设为 1)允许把 TIME_WAIT socket 用于新 outgoing 连接(client 场景有效),但服务端 listening socket 不走这条路,基本无效;tcp_tw_recycle 已在 4.12+ 内核移除,且早年开启会导致 NAT 环境下连接失败,绝对不要碰。
- 唯一较安全的调优:增大本地端口范围,
echo "1024 65535" > /proc/sys/net/ipv4/ip_local_port_range(注意避开 1–1023 特权端口) - 监控必须跟上:用
watch -n 1 'ss -tan state time-wait sport = :8080 | wc -l'持续观察,再配合业务 QPS 看相关性 - 如果服务本身是反向代理(如用 C++ 写的网关),还要检查 upstream 连接是否也频繁主动关闭——那才是真正的
TIME_WAIT生成源
排查时最容易卡在“以为调了 SO_REUSEADDR 就万事大吉”,其实它不解决生成量,只缓解绑定冲突;真正要盯住的是代码里哪一行调用了 close()、谁在发 FIN、连接是不是真的需要那么短命。


















