存在无主ESTABLISHED连接:进程崩溃或未调用close()退出时,内核不回收socket,连接卡在ESTABLISHED状态且无PID关联;可用ss -tunp state established | grep -v "pid="验证。

确认是否存在无主 ESTABLISHED 连接
进程崩溃或未调用 close() 就退出时,内核不会自动回收其 TCP socket,这些连接会卡在 ESTABLISHED 状态,且不关联任何 PID。最直接的验证方式是:
-
ss -tunp state established | grep -v "pid="—— 若有输出,说明存在“孤儿连接” -
ss -tun state established | wc -l和ps aux | wc -l对比:连接数远大于进程数(比如 5000+ ESTABLISHED 但只有 200 个活跃进程),大概率就是残留问题 - 检查具体端口是否被占:如服务监听
:8080却 bind 失败,可用ss -tuln sport = :8080查看是否被残留 socket 绑定
优先终止关联进程(最安全)
如果 ss -tunp 能查到 PID,说明连接仍由某进程持有,只是该进程可能僵死、卡住或响应迟缓。此时应先尝试优雅终止:
- 用
kill -15发送 SIGTERM,给应用机会清理资源 - 若 10 秒后连接仍在,再用
kill -9强杀 —— 内核会在进程彻底消亡后回收其所有 socket - 注意:不要只 kill 子进程而漏掉父进程(比如 systemd 服务下,
systemctl restart xxx比手动 kill 更可靠)
强制清除单条无主 ESTABLISHED 连接(kernel ≥ 4.15)
当确认连接已无对应进程(ss -tunp 不显示 pid),又不能重启整个服务时,可借助 ss -K 主动发 RST 包中断它:
- 先定位目标:如想清掉连向
192.168.1.100:8080的 ESTABLISHED 连接,执行:ss -K dst 192.168.1.100:8080 - 支持更细粒度匹配,例如按本地端口:
ss -K dport = :8080 - ⚠️ 前提:系统内核 ≥ 4.15 且 iproute2 ≥ 5.0.0;否则会报错
Unknown option: K - 该操作不依赖进程状态,直接作用于内核 socket 表,但属于“外科手术”,生产环境慎用,避免误杀关键长连接
避免复发:代码和系统层双加固
清理只是救火,根源在程序没正确管理连接生命周期:
- 应用层必须显式调用
close()(Go 的defer conn.Close()、Python 的with socket...、Java 的try-with-resources) - 设置合理的 socket 超时:
setsockopt(SO_KEEPALIVE)+ 调整/proc/sys/net/ipv4/tcp_keepalive_time(默认 7200s 太长,建议设为 600–1800) - 避免短连接高频复用同一端口:客户端可启用
SO_REUSEADDR,服务端注意 TIME_WAIT 回收(net.ipv4.tcp_tw_reuse = 1) - 监控项要加一条:
ss -tun state established | wc -l持续告警,阈值建议设为最大可用端口数的 60%
真正棘手的不是怎么删,而是删完一小时又涨回来——那说明你还没找到那个忘了 close() 的 goroutine 或线程。










