Connection reset by peer、Connection reset、连接被重置——这几个字几乎每个运维和开发都见过。它的技术含义很明确:TCP 连接收到了对端或中间设备发来的 RST 包,连接被强制、非正常地终止了。但"为什么会收到 RST"的答案五花八门,可能是服务端没监听、应用崩了、防火墙重置、负载均衡健康检查踢掉、也可能是连接被闲置超时后又拿来复用。
这篇文章帮你把 RST 的来龙去脉理清楚:先讲 RST 意味着什么,再逐类分析常见原因,然后给出一套用 tcpdump、ss 和日志组合定位的方法,最后给出解决方向。目标是让你下次遇到"连接被重置"时,能快速判断是网络、系统还是应用的问题。
一、RST 到底意味着什么
TCP 用 RST 标志表示"复位连接"。它和 FIN 的区别是:FIN 是优雅关闭,双方还会走完四次挥手;RST 是立即、单方面终止,收到 RST 的一方会直接丢弃连接状态,之前没有交付的数据全部作废。
会收到 RST 的场景主要两类:
- 连接不存在或不被接受。你往一个没有监听的端口发包,对端内核会回 RST;你往一个已经关闭的连接发数据,对端也会回 RST。
- 主动拒绝。中间设备(防火墙、负载均衡)、应用或内核因为策略、超时、异常而主动发 RST。
所以看到 RST,先别急着怪网络,它其实是一个"明确的拒绝信号"——比无声丢包要好排查得多。
二、常见原因分类
原因一:目标端口没有服务在监听
最经典的情况。你 telnet 或 curl 一个端口,立刻收到 Connection refused(连接被拒绝),抓包会看到一个干净的 RST。这不是"重置",是内核明确告诉你"这个端口没人听"。用 ss 确认:
# 看服务是否在监听目标端口
sudo ss -lntp | grep ':8080'
# 或从客户端侧测试连通性
curl -v telnet://10.0.0.8:8080
nc -vz 10.0.0.8 8080
服务没起来、监听到了错误的地址(比如只监听 127.0.0.1 没监听 0.0.0.0)、或监听了 IPv6 而客户端连 IPv4,都会导致这个问题。
原因二:应用崩溃或异常退出
连接建立得好好的,传输中途突然 RST,往往是应用进程崩了、被 OOM 杀了、或线程异常导致 socket 被关闭而缓冲区里还有未发送/未读取的数据。进程带着未处理的数据退出时,内核会发 RST 而不是 FIN。
排查方向:看应用日志有没有崩溃堆栈、看系统日志有没有 OOM 记录。
# 看是否有 OOM 杀进程
sudo dmesg -T | grep -i -E 'oom|killed process'
# RHEL/CentOS/Rocky / Debian/Ubuntu 都可用
sudo journalctl -k | grep -i -E 'oom|out of memory'
# 看应用自己的错误日志
sudo journalctl -u <服务名> --since '1 hour ago'
原因三:防火墙或中间设备重置连接
防火墙、安全组、WAF、负载均衡可能因为策略直接发 RST 掐断连接。典型场景:连接空闲一段时间后被防火墙会话表清理(TCP 会话老化),之后再用这条连接发数据,就会收到 RST;或者某些安全设备对可疑流量直接重置。
这类问题的特征是:空闲一段时间后第一次操作必然失败,重连后又能用一阵。定位方法是分别在客户端和服务端抓包,看 RST 是从哪个方向、哪个 IP 发出来的——如果 RST 来自一个既不是客户端也不是服务端的 IP,那基本就是中间设备。
原因四:连接被闲置超时后复用
应用用了连接池,但连接池没有做健康检查,里面的连接其实已经被对端或中间设备关闭了。应用拿着这条"实际上已死"的连接发请求,对端回 RST,就报 Connection reset。
这是高发问题,尤其在后端配了 Nginx、云负载均衡的场景里更常见,因为负载均衡往往有空闲超时(比如 60 秒)。解决方向:连接池开启保活检测(test-on-borrow / keepalive 探测),并把池的空闲超时设置得比负载均衡的超时更短。
原因五:负载均衡健康检查与连接踢除
负载均衡器会定期对后端做健康检查,可能用短连接。如果配置不当,或者后端 max_connections 被打满,新连接会被 LB 直接 RST。表现是间歇性、部分请求失败,而不是全挂。
原因六:SYN 队列满或半连接被丢弃
服务端 SYN 队列满时,可能直接丢弃 SYN,也可能给部分连接回 RST。这种在高并发建连时出现,通常伴随"Connection reset during handshake"之类的报错。用 nstat 或 netstat -s 看丢弃计数:
nstat -az | grep -i -E 'ListenOverflows|ListenDrops|TCPBacklogDrop|SYN'
三、按"谁发 RST"分类
定位 RST 最有效的方法是先确定 RST 是谁发的,这能立刻把范围缩小一半:
| RST 来源 | 典型特征 | 可能原因 |
|---|---|---|
| 目标服务器 | RST 的源 IP 就是服务端 | 端口没监听、应用崩溃、队列满 |
| 客户端本机 | RST 源 IP 是客户端 | 连接已关闭仍发数据、应用异常 |
| 中间设备 | RST 源 IP 既非客户端也非服务端 | 防火墙、LB、WAF 策略重置 |
| 云安全组/NAT | 表现为超时或重置,包可能不可见 | 策略拦截、会话老化 |
四、排查步骤与命令
第一步:在客户端和服务端同时抓包
# 客户端侧抓包:抓与目标之间的 TCP 包,看 RST 从哪来
sudo tcpdump -i eth0 -nn -S 'host 10.0.0.8 and tcp port 8080'
# 服务端侧抓包:同样条件
sudo tcpdump -i eth0 -nn -S 'host <客户端IP> and tcp port 8080'
# 只看 RST 包,快速定位
sudo tcpdump -i eth0 -nn -S 'tcp[tcpflags] & tcp-rst != 0'
比对两侧输出:如果客户端看到 RST 但对侧根本没发出 RST,说明 RST 是中间设备伪造的;如果服务端确实发出了 RST,再结合服务端日志判断原因。
第二步:看连接状态和进程
# 看当前连接,重点看是否有大量 CLOSE_WAIT、SYN_RECV
sudo ss -antp
# 按状态统计
ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
# 看目标端口的监听情况
sudo ss -lntp | grep ':8080'
# 看是否有重传(连接质量差也可能触发重置)
ss -anti | grep -E 'retrans|timer'
第三步:查日志
# 系统日志(内核、OOM、防火墙)
sudo journalctl -xe --since '30 min ago'
sudo dmesg -T | tail -50
# 应用日志
sudo journalctl -u <服务名> --since '30 min ago' | tail -100
# 防火墙日志(可能记录了被拒绝的连接)
# RHEL/CentOS/Rocky:
sudo grep -i 'REJECT\|DROP' /var/log/messages
# Debian/Ubuntu:
sudo grep -i 'REJECT\|DROP' /var/log/syslog
# RHEL/CentOS/Rocky 查看 firewalld 规则
sudo firewall-cmd --list-all
# Debian/Ubuntu 查看 ufw 规则
sudo ufw status verbose
第四步:检查空闲超时相关配置
如果现象是"空闲一会儿就重置",重点查这几处的超时设置是否不一致:
- 负载均衡 / 云 LB 的空闲超时。
- Nginx 的
keepalive_timeout、proxy_read_timeout。 - 应用连接池的 idle timeout、max lifetime。
- 中间防火墙的 TCP 会话老化时间。
原则:应用侧的空闲超时要比中间设备的短,这样应用会主动重建连接,而不是拿着已死的连接去撞 RST。
五、解决方向
按原因对应处理:
- 端口没监听:把服务起起来,确认监听地址和端口正确(
bind 0.0.0.0而非仅127.0.0.1),防火墙放行对应端口和协议(TCP/UDP 别搞混)。 - 应用崩溃:定位崩溃原因,限制内存避免 OOM,修复异常处理;回归测试时用压力工具复现。
- 防火墙/LB 重置:调整会话老化时间或加白名单;确认规则没有误伤正常流量。改防火墙前先备份规则。
- 连接被复用撞死:连接池开启健康检查,缩短空闲超时;对 HTTP 客户端启用 keep-alive 并配合重试。
- 队列满:调大
somaxconn和应用 backlog,提升 accept 能力,必要时加机器。
# 放行端口示例
# RHEL/CentOS/Rocky(firewalld)
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
# Debian/Ubuntu(ufw)
sudo ufw allow 8080/tcp
sudo ufw reload
风险提示:修改防火墙规则、负载均衡策略、内核参数都会直接影响线上流量。操作前备份(
iptables-save、firewall-cmd --runtime-to-permanent前的配置、sysctl -a关键项),在低峰期执行,并准备好回滚方案。抓包含敏感数据,分析完及时清理。
六、常见误区
误区一:Connection reset 就是网络不稳定。多数情况下它是明确的拒绝信号,先看 RST 从哪来,而不是笼统归咎于网络抖动。
误区二:重试就能解决。不加控制的重试可能放大问题,尤其是对端已过载时。要配合退避和连接池健康检查。
误区三:只在服务端抓包就够了。很多 RST 由中间设备产生,只在服务端抓可能什么都看不到。两侧同时抓才能看清全貌。
误区四:把 Connection refused 和 Connection reset 混为一谈。前者是端口没监听(连接阶段就被拒),后者是连接已建立后被中途复位,排查方向不同。
常见问题
Q:Connection reset by peer 的 peer 一定指服务器吗?
A:不一定。"peer"指对端,可能是服务器,也可能是中间设备或客户端自己。要通过抓包看 RST 的真实来源。
Q:偶尔才出现一次连接重置,怎么抓?
A:用 tcpdump 落盘循环抓包(-w 配合 -C/-W),持续一段时间,复现后回放分析;或直接抓 RST 过滤条件,减小文件体积。
Q:为什么空闲一段时间后第一次访问必然失败?
A:典型的会话老化。中间设备或对端清理了空闲连接,而应用还在用旧连接,自然收 RST。让应用侧空闲超时小于中间设备即可。
Q:SSH 长时间不动就断开,也是这个原因吗?
A:常见于中间 NAT/防火墙老化 TCP 会话。可在客户端配置 ServerAliveInterval 定期发心跳保持连接活跃。
Q:改内核参数能解决 RST 吗?
A:多数 RST 来自应用或中间设备,内核参数帮助有限。只有队列满、端口耗尽这类系统层原因才需要调参,别把参数当万能药。
总结
Connection reset的本质是收到了 RST,它是明确的复位/拒绝信号,比无声丢包更容易定位。- 常见原因:端口没监听、应用崩溃或 OOM、防火墙/中间设备重置、连接被闲置后复用、负载均衡健康检查踢除、队列满。
- 排查的核心是先确定 RST 由谁发出:在客户端和服务端同时抓包,用
tcpdump过滤 RST,再看ss -antp的连接状态和系统、应用日志。 - 解决要因因对症:端口问题补监听、崩溃问题查内存与异常、会话老化问题调整超时并加连接池健康检查、队列问题调 backlog。
- 一切涉及防火墙、负载均衡、内核参数的改动,先备份、低峰操作、准备回滚;抓包文件注意敏感信息,用完及时删除。
企业QQ咨询




