长连接接入后被频繁断开,绝大多数不是玄学,而是心跳、超时参数和网络环境三个环节没对齐,按“抓日志找证据→调TCP和业务参数→加重连保护”的顺序处理,基本能解决九成以上问题。
长连接频繁断开怎么排查
先把日志按“谁断开、为什么断、什么时候断”归类
接入长连接后第一个晚上就收到一堆告警,别急着改参数,先打开客户端和服务端的日志,把断连记录分成三类:主动断开、被动断开、超时消失。
- 主动断开:日志里有明确的 close 或 FIN 包记录,通常是代码里调用了关闭方法,或者业务侧主动踢掉连接。
- 被动断开:出现 RST 包,多半是服务端进程崩溃、端口不可达,或者TCP协议栈异常。
- 超时消失:没有正常挥手记录,连接像人间蒸发,大概率是中间网络设备静默丢包,或者长时间空闲被防火墙清理。
常见的排查路径是先在客户端和服务端各抓一份日志,比对断开时间点,如果客户端显示“连接被重置”,服务端日志却干干净净,说明包没到服务端,问题在网络链路,如果服务端有“连接空闲超时”的记录,那就要检查自己的心跳间隔和空闲阈值了。
用抓包确认断开方和真实原因
日志只能告诉你“连接没了”,抓包能告诉你“连接是怎么没的”,直接在服务端跑 tcpdump 监听业务端口:
tcpdump -i eth0 'tcp port 8080' -w /tmp/longconn.pcap
等断开事件复现后,用 Wireshark 打开,重点看断开前最后一个包的 TCP Flags:
- FIN,ACK:正常关闭,谁发 FIN 谁主动断。
- RST:异常重置,可能是程序崩溃、端口复用、或者对端认为连接非法。
- 重传包:说明中间网络丢了包,重传超过阈值后连接被强制丢弃。
抓包是最有说服力的,不要凭感觉猜。
用连接统计快速定位异常基数
如果问题表现为“整体不稳定”,不是零星个别连接,那就看连接状态的分布,执行这两条命令:
ss -ant | awk '{print $1}' | sort | uniq -c
netstat -s | grep -i "resets"
第一条命令统计当前所有TCP连接的状态数量,TIME_WAIT 的数量异常庞大,说明短连接和长连接混跑,端口资源被临时连接占满,SYN_SENT 大量堆积,说明服务端接受队列溢出,第二条命令看 “connections reset” 的数量增长情况,如果数字在持续上涨,说明确实有反复重置发生。

行业共识是:单机长连接超过5万时,最主要的问题往往不在业务代码,而在内核参数和文件描述符限制。
长连接心跳机制和重连策略怎么设置
心跳间隔和超时阈值之间的计算关系
很多开发者在接入长连接时,把心跳设计成了“每30秒发一次,60秒没回就断开”,这个配置不能说错,但忽略了中间网络设备的因素。
大部分云厂商的负载均衡和防火墙,对空闲连接的超时时间集中在300到600秒,你心跳发得再勤快,只要TCP层空闲时间超过这个阈值,连接照样被清理,反过来,心跳并不是越勤越好,每5秒一次的心跳会造成大量无效网络包,白白消耗带宽和CPU。
合适的做法是给心跳留出冗余:
- 心跳间隔设置为空闲超时时间的1/5到1/3。
- 超时次数达到3次才判定连接失效。
- 如果网络环境复杂,比如跨地域专线或者移动网络,间隔再适当压缩。
WebSocket断线重连间隔设置多少合适
WebSocket是长连接里最常见的落地协议,关于重连间隔,业内实践比较成熟的方案是指数退避加上限封顶:
第1次重连:1秒后 第2次重连:2秒后 第3次重连:4秒后 第4次重连:8秒后 ... 上限:60秒,之后保持60秒间隔重试
同时加上随机抖动,固定间隔重连会在断网恢复瞬间产生“惊群效应”,成百上千个客户端同时高峰期重连,直接把服务端冲垮,加一个 0 到 3000 毫秒的随机偏移,能让重连请求分摊开。
如果你用的是 iOS 或 Android 原生客户端,注意系统级的网络切换(比如Wi-Fi切蜂窝网络)会强制断开TCP连接,这不是代码问题,但需要在业务层做网络状态监听,网络恢复后主动重建长连接。
重连保护机制要拦住三层
重连策略不能只写“断线就重连”,还得考虑服务端是否已经挂了。
- 第一层:重连前先探测服务端口是否可达(TCP connect 或 HTTP HEAD)。
- 第二层:重试次数累计达到阈值时,切换到备用域名或备机IP。
- 第三层:连续重连失败超过5次,停止自动重连,弹出用户引导或上报监控,等待手动恢复。

没有这三层保护的重连逻辑,本质上就是让所有客户端拿自己的流量去“轰炸”一个已经出故障的服务端。
长连接服务端参数调优
TCP层参数优先于业务层参数
长连接接入后频繁断开,服务端首先要检查的是TCP层面的三个内核参数:
sysctl net.ipv4.tcp_keepalive_time sysctl net.ipv4.tcp_keepalive_intvl sysctl net.ipv4.tcp_fin_timeout
TCP keepalive 默认的 tcp_keepalive_time 是 7200 秒,也就是说内核默认要等2小时才探测一次空闲连接,如果你业务层心跳是30秒,TCP层这个参数其实影响不大;但如果业务层没用心跳,那就得把 tcp_keepalive_time 调小到 600 秒左右。
tcp_fin_timeout 默认 60 秒,如果短连接和长连接混存在同一台服务器,这个值可以调小到 30 秒,避免处于 FIN_WAIT_2 状态的废连接堆积。
文件描述符和连接数限额
长连接是“占用”型的资源,不像短连接用完即走,单机上几十万长连接,每个连接至少占用一个文件描述符,检查这些限制:
ulimit -n sysctl net.core.somaxconn sysctl net.ipv4.ip_local_port_range
ulimit -n 对应进程的文件描述符上限,高并发业务建议至少 65535 起步。somaxconn 是监听队列长度,默认 128 往往不够,建议调大。ip_local_port_range 影响客户端主动发起连接时可用端口范围,如果短连接量大,把这个范围扩大能减少端口耗尽导致的连接失败。
核心参数参考对比
| 参数 | 推荐值 | 作用 |
|---|---|---|
| tcp_keepalive_time | 600s | 缩短空闲连接探测周期 |
| tcp_fin_timeout | 30s | 快速回收关闭状态的连接 |
| somaxconn | 1024 | 缓解连接风暴 |
| ulimit -n | 65535+ | 支撑更多并发长连接 |
| 心跳间隔 | 30s~60s | 维持连接活跃 |
| 重连上限间隔 | 60s | 防止重连风暴 |
一个线上案例:长连接接入一周后每天凌晨断开

之前帮一位做物联网网关的朋友排查过类似问题,业务接入的是百度服务器上的长连接服务,客户端每15秒上报一次数据,接入后一周内表现正常,随后开始每天凌晨2点到3点集中断开。
排查过程其实不复杂,先抓包,发现断开前服务端和客户端之间有大量TCP重传,而且重传的是同一段数据,由于凌晨2点正好是很多业务系统跑批备份的时间,带宽被大流量占满,数据包在网关设备上排队超时被丢弃。
调整方案分两步:客户端心跳从15秒加密到5秒,让连接活跃度更高,减小被网络设备判定为空闲的风险;服务端开启了TCP的用户态超时重传参数调整,允许更多次重传尝试,第二天凌晨断开彻底消失,连续观察一周没有再复现。
这个案例说明,长连接调优不只是调服务器,还要考虑业务流量周期,凌晨跑批这种场景,用地域来定义就是“云服务器上的长连接在半夜总掉线”,提前和运维确认网络高峰期,比事后调参更有效。
常见问题
长连接断开后客户端一直重连失败怎么办
先区分是服务端不可达还是客户端重连逻辑有缺陷,在客户端机器上手动执行 telnet 服务端IP 端口,或者用 nc -vz 服务端IP 端口 来验证,如果手动连接成功但程序重连失败,检查程序里是否复用了旧的 socket 实例;如果手动连接也失败,检查服务端进程状态和防火墙规则,重点看是否触发了 IP 封禁或限流策略。
长连接心跳机制和重连策略到底怎么选才算合理
判断标准是:在网络正常的情况下,连接能稳定保持数小时不断开;在网络异常恢复后,客户端能在数十秒内自动恢复连接,同时服务端在断开期间不会积累大量 TIME_WAIT 或 CLOSE_WAIT 状态连接,满足这三条,心跳间隔和重连策略就是合理的。
接入长连接后服务器内存只涨不降是正常现象吗
不正常,长连接虽然会持续占用内存,但稳定运行后内存占用应该是一条水平线,如果内存持续增长,大概率存在连接泄漏,比如每次断线重连都创建新连接而没有关闭旧连接,在服务端执行 ss -s 对比总连接数是否持续增长,确认是连接数增长还是单连接内存消耗增长,前者是代码问题,后者需要排查缓冲区或消息队列堆积。