TIME_WAIT端口耗尽需先确认:用ss -ant|grep TIME_WAIT|wc -l查数量(上万危险),ss -s看timewait行及orphans,cat /proc/net/sockstat查tw计数;若connect()报Cannot assign requested address即为端口枯竭。

怎么看是不是 TIME_WAIT 真的撑爆了端口
别急着改内核参数,先确认问题根源。Linux 上每个四元组(源IP+源端口+目的IP+目的端口)唯一标识一个连接,而 TIME_WAIT 状态会占用源端口 60 秒(2MSL),短连接高频发起时,很容易把本地可用端口(默认 net.ipv4.ip_local_port_range 通常是 32768–65535,共约 32K)耗尽。
查法很直接:
-
ss -ant | grep TIME_WAIT | wc -l—— 看当前数量,上万就危险了 -
ss -s—— 输出汇总,重点关注timewait行和orphan数量 -
cat /proc/net/sockstat—— 看sockets: used和tcp:下的tw计数 - 如果
connect()开始返回Cannot assign requested address错误,基本就是端口枯竭,不是内存或 fd 限制
为什么 C++ 客户端短连接特别容易撞上这坑
很多 C++ 网络库(比如基于 libcurl、Boost.Beast 或手写 socket()+connect())默认不复用连接,每次 HTTP 请求都新建 TCP 连接,发完立刻 close()。这时主动关闭方(客户端)进入 TIME_WAIT,且无法绕过——这是 TCP 协议强制要求,防止旧包干扰新连接。
关键点:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
- 服务端是否开启
SO_LINGER并设为 0?别这么干。这会让客户端跳过TIME_WAIT,但可能丢 FIN 包、破坏可靠性,还可能让服务端收到 RST,得不偿失 - 客户端调用
close()后,内核才开始计时;shutdown(SHUT_WR)不会触发TIME_WAIT,但它只是半关,对方仍可发数据,对 HTTP 等请求响应模型没意义 - 别指望
setsockopt(..., SO_REUSEADDR)让你重用TIME_WAIT套接字——它只允许bind()时复用 *本地地址+端口*,前提是对方四元组不同(比如连不同服务器),对同一目标无效
真正有效的三个落地解法
改代码比调内核更可控、副作用更小:
-
加连接池:哪怕最简实现,维护一个
std::queue<int>存活 socket,设置空闲超时(比如 5 秒),复用前send(…, MSG_DONTWAIT)检一下是否还通。HTTP/1.1 默认支持 keep-alive,加Connection: keep-alive头就行 -
换目标 IP 分散端口压力:如果后端是集群,客户端轮询多个 VIP 或域名(DNS 返回多 A 记录),让连接分散到不同
dest_ip:dest_port,四元组不同,TIME_WAIT就不打架 -
必要时调内核(仅限压测或边缘场景):
•
net.ipv4.tcp_tw_reuse = 1—— 允许把处于TIME_WAIT的套接字用于新连接(需时间戳开启,且对方也支持),仅适用于客户端; •net.ipv4.tcp_timestamps = 1(必须开); •net.ipv4.tcp_fin_timeout不建议调小——它只影响CLOSE_WAIT,对TIME_WAIT无效
TIME_WAIT 不是 bug,是设计权衡
它保证了网络的鲁棒性,强行消灭只会转移问题。排查时最容易忽略的是:你以为是端口不够,结果是 ulimit -n 限制了 fd 数量,或者 DNS 解析慢导致连接堆积,又或者服务端 accept() 队列溢出(net.core.somaxconn 太小)造成客户端 connect() 超时重试,进一步加剧短连接频率。先看 ss -s 里的 orphans 和 mem 行,再动手。

















