可以使用 ss -tan | awk '$1~/tcp/&&$NF~/SYN_RECV|FIN_WAIT2|CLOSE_WAIT/{print $NF}' | sort | uniq -c 来统计半开连接数量及分类。这里的 SYN_RECV 表示 TCP 三次握手尚未完成;而 FIN_WAIT2 和 CLOSE_WAIT 往往说明对端连接异常,或者应用程序没有按预期正常关闭连接。

怎么用 netstat 或 ss 统计半开连接(SYN_RECV、TIME_WAIT 等)数量
Linux 系统不会直接给网络连接标记“这是由对端异常导致的半开连接”,但我们可以通过 TCP 状态码识别常见的半开连接场景。例如 SYN_RECV 表示服务端已经收到 SYN 请求,但 TCP 三次握手还未完成,常见原因包括客户端丢包、客户端异常退出或网络中断;FIN_WAIT2 表示本端已经发出 FIN 包,正在等待对端返回 FIN,如果该状态长时间不消失,通常意味着对端异常中断;CLOSE_WAIT 则表示对端已关闭连接,但本端应用迟迟没有执行 close,这种情况在应用程序 bug 或连接未及时释放时非常常见。总体来看,这几种 TCP 状态一旦持续堆积,往往就是排查 Linux 半开连接、异常连接和 TCP 连接泄漏时最重要的判断依据。
统计命令通常需要先过滤 TCP 连接,再按状态进行分组统计:
ss -tan | awk '{print $1}' | sort | uniq -c | sort -nr—— 快速统计所有 TCP 状态及对应数量(推荐使用,通常比 netstat 更快也更准确)netstat -an | awk '$6 ~ /SYN_RECV|TIME_WAIT|FIN_WAIT2|CLOSE_WAIT/ {print $6}' | sort | uniq -c—— 适合兼容老版本 Linux,但要注意netstat在部分发行版默认未安装,而且输出格式容易受到 locale 环境影响- 如果需要排除本地回环地址和已建立连接,可增加过滤条件:
ss -tan | awk '$1 ~ /tcp/ && $NF ~ /SYN_RECV|FIN_WAIT2/ && $4 !~ /127.0.0.1|::1/ {print $NF}' | sort | uniq -c
为什么不能只看 TIME_WAIT 就认定是“对端异常”
TIME_WAIT 本质上是 TCP 协议中的正常状态,用于避免旧数据包影响新连接,持续时间通常约为 2×MSL(一般约 60 秒)。对于高并发 Web 服务、API 服务或短连接业务来说,大量 TIME_WAIT 是常见现象,并不直接代表网络故障或客户端异常。只有在以下几类组合场景中,才需要重点怀疑连接异常:
- 同一源 IP + 端口被频繁复用,同时
TIME_WAIT数量短时间内明显激增(可能说明客户端没有正确复用连接) SYN_RECV持续超过 30 秒仍不下降,并且对应源 IP 在防火墙日志中看不到后续 ACK(说明 SYN 已到达,但 ACK 丢失或对端未发送)CLOSE_WAIT数量持续上涨,同时相关进程的 CPU 和内存指标没有明显变化(大概率是应用未正确 close socket)
因此,单独依赖某一个 TCP 状态计数,无法直接下结论说是“对端异常导致的半开连接”,还必须结合系统日志、时间序列变化、源 IP 分布和业务行为一起交叉分析。
从 auth.log、kern.log、iptables 日志里捞半开连接线索
系统日志通常不会直接写出“半开连接”几个字,但会留下很多能够辅助判断对端异常、TCP 连接中断或握手失败的间接证据:
grep "Connection timed out" /var/log/auth.log—— SSH 客户端超时断开后未及时清理,可能会遗留FIN_WAIT2状态连接grep -i "reset" /var/log/kern.log | tail -50—— 查看内核输出的 connection reset 信息,常常伴随着SYN_RECV突然下降或ESTABLISHED连接异常关闭grep "DPT=.*DROP" /var/log/iptables.log 2>/dev/null | awk '{print $8}' | sort | uniq -c | sort -nr | head -10—— 如果某个 IP 被大量 DROP,同时此前又频繁出现在SYN_RECV列表中,极可能是网络中间设备拦截、链路不稳定或对端防火墙策略发生了变化
需要注意的是:iptables 日志必须提前配置 LOG 规则,例如 -A INPUT -p tcp --syn -j LOG --log-prefix "SYN-LOG: ",否则日志中不会留下 SYN 包记录,也就无法进一步分析 TCP 半开连接来源。
用 shell 脚本做轻量级半开连接分类告警
很多场景下没必要依赖复杂的 ELK 或全套监控平台,一个二十行左右的 Shell 脚本就足以完成 Linux 半开连接统计、状态分类和基础告警:
#!/bin/bash SYN_RECV=$(ss -tan state syn-recv | wc -l) FIN_WAIT2=$(ss -tan state fin-wait-2 | wc -l) CLOSE_WAIT=$(ss -tan state close-wait | wc -l)if [ $SYN_RECV -gt 50 ]; then echo "$(date): HIGH SYN_RECV ($SYN_RECV)" >> /var/log/net-alert.log logger -t netwatch "SYN_RECV > 50" fi
if [ $CLOSE_WAIT -gt 200 ]; then echo "$(date): CLOSE_WAIT surge ($CLOSE_WAIT), check app process" >> /var/log/net-alert.log
可追加:lsof -i -n -P | grep CLOSE_WAIT | head -5 >> /var/log/net-alert.log
fi
关键点:
- 优先使用
ss -tan state xxx进行状态统计,比基于正则的文本匹配更稳定可靠,也能避免字段误判 - 告警阈值必须结合具体业务进行调优:例如 Web 服务中
SYN_RECV> 50 可能已经比较危险,但在高并发内网 RPC 网关中,常态超过 200 也并不少见 - 日志文件建议固定写入
/var/log/net-alert.log,避免输出到 /tmp 后因临时目录清理而丢失告警记录
真正困难的地方从来不是把半开连接数量统计出来,而是如何把 SYN_RECV 的异常波动与某次上游 CDN 节点故障、某批 Android 客户端升级后 TLS 握手失败、或者某台负载均衡器 conntrack 表溢出这些事件关联起来——这要求你在平时记录日志时就带上足够的上下文,而不是等告警触发后再临时回头排查。
