服务端正常启动但客户端请求不到:网络可达性排查全链路
一句话结论(30s)
服务端「启动正常但连不上」90% 的问题不在代码,而在网络可达性链路——因为从 IP 层到端口层到防火墙到监听地址到 SELinux 再到容器/代理,任何一层拦截都会让请求到不了服务,所以应按 ping→telnet→防火墙→监听→SELinux→代理 的清单逐层排查,而非盲目重启。
核心原理(2min)
- 六层排查:
ping(IP 层,ICMP)→telnet/nc(端口层,TCP)→ 防火墙(INPUT/FORWARD/OUTPUT 链)→netstat/ss(监听地址 0.0.0.0 vs 127.0.0.1)→ SELinux(端口白名单)→ 代理/NAT(容器端口映射、K8s Service、Ingress)。 - 关键判断:telnet「Connection refused」= 端口没监听;「连接超时」= 包被中间设备丢弃(大概率防火墙)。
- 高频陷阱:
--permanent忘加重启丢失、Docker 覆盖 iptables 规则、云安全组未放行、框架默认绑定 127.0.0.1。
底层深入(5-10min)
开发环境调试时碰到的经典场景:服务端显示”Started Application in 5.6 seconds”,一切都正常,客户端却连不上。日志干干净净,没有错误也没有异常——问题不在代码,在代码写的代码之外。
第一关:IP层是否可达 —— ping
首先要回答的问题是——两台机器之间能不能通信?
ping <server_ip>
ping通了说明IP层没问题。如果ping不通:
- 两台机器是否在同一个网段?
- 中间有没有路由器/交换机故障?
- 服务器是否开启了禁ping(内核参数
net.ipv4.icmp_echo_ignore_all=1)?
注意:ping使用的是ICMP协议,不是TCP或UDP。ping通只能证明IP层可达,不能证明TCP端口可达。
思考:为什么「ping 通了」还不能说明问题?因为 ping 走的是 ICMP,TCP 端口走的是另一条链路。ping 通只证明「IP 层可达、机器还活着」,但目标端口的监听、防火墙对端口的放行是另一回事——就好比电话拨通(IP 通)不代表对方接了你想找的那个分机(端口通)。
第二关:端口是否可达 —— telnet / nc
服务监听的是TCP端口,ping通了不代表端口也通。
telnet <server_ip> <port>
# 或
nc -zv <server_ip> <port>
telnet连接成功(显示Connected / Escape character)说明TCP三次握手完成,端口层可达。如果连接失败(Connection refused),说明端口没监听;如果卡住不动(连接超时),说明包被中间某个设备丢弃了——大概率是防火墙。
思考:为什么
Connection refused和「连接超时」指向完全不同的结论?因为 refused 是「对方机器明确回了 RST」——说明机器通、但那个端口没人监听;超时是「发出去的包石沉大海」——说明包被中间某个设备(大概率防火墙)悄悄丢掉了。一个告诉你「服务没起来」,一个告诉你「路上有拦截」,排查方向截然不同。
第三关:防火墙策略 —— iptables / firewalld
这是最常见的罪魁祸首。Linux上的防火墙规则可能非常复杂:
# 先看防火墙状态
systemctl status firewalld # 或 ufw status
# 检查iptables规则
iptables -L -n -v # filter表
iptables -t nat -L -n -v # NAT表
# 检查是否开了对应端口
firewall-cmd --list-all # firewalld方式
重点看三条链:
- INPUT链:进入本机的包。如果这里DROP了端口,外部请求直接丢弃,客户端看到的就是超时。
- FORWARD链:转发包。如果服务在容器里,这是关键。
- OUTPUT链:本机发出的包。有些防火墙连出站都限制,极少见但可能存在。
常见防火墙陷阱:
- 添加了端口规则但忘了
--permanent,重启后规则丢失。 - Docker自动添加了iptables规则,修改了iptables但被Docker覆盖。
- 云服务器除了系统防火墙,还有安全组(阿里云/腾讯云/AWS),需要在控制台层面放行端口。
第四关:服务到底监听在哪 —— netstat / ss
服务启动成功不代表监听对了地址和端口:
netstat -tlnp | grep <port> # 传统工具
ss -tlnp | grep <port> # 更快的ss
重点看Local Address这一列:
0.0.0.0:8080:监听所有网络接口(包括外网IP),外部可以访问。127.0.0.1:8080:只监听回环地址,外部无法访问。这是另一个常见的坑——很多框架默认绑定127.0.0.1。
思考:为什么监听
127.0.0.1就「外部无法访问」?因为回环地址只存在于本机内部,代表「这台机器自己」。服务绑到回环上,等于只对本机进程开放;外部请求的包到达网卡后,内核发现目的地不是本机监听的任何「对外地址」,直接丢弃。很多框架默认绑定 127.0.0.1,正是「本地能访问、外部连不上」的高频元凶。
192.168.x.x:8080:只监听特定内网IP,外网同样访问不到。
第五关:SELinux —— 隐藏的访问控制
在CentOS/RHEL系统上,SELinux是另一道难以察觉的屏障:
getenforce # 查看SELinux状态
semanage port -l | grep http # 查看SELinux允许的端口
SELinux有自己的端口白名单。例如,HTTP服务默认只允许80和443端口。如果你把Nginx改到8080端口,iptables放行了,但SELinux依旧拦截,客户端也连不上。
快速验证是否是SELinux的问题:
setenforce 0 # 临时关闭(仅测试用,不要在生产环境这样做)
# 如果能连了,就是SELinux问题,需要添加策略而非永久关闭
semanage port -a -t http_port_t -p tcp 8080 # 正确做法
第六关:网络代理与NAT
如果服务在容器(Docker/K8s)里运行,排查链路更长:
- 宿主机端口映射是否正确?
docker ps看PORTS列。 - K8s Service的ClusterIP/NodePort/LoadBalancer配置是否正确?
- Ingress Controller是否正确路由?
如果客户端经过了代理(公司代理/Nginx反向代理/API网关),也要检查代理是否把请求转发到了正确的地方。
排查之外:建立”可达性心理清单”
每次遇到”连不上”,不要盲目重启服务或重启服务器。按顺序走一遍:
ping通了吗? → telnet通了吗? → 防火墙放行了吗?
→ 监听地址对吗? → SELinux关闭/配置了吗? → 代理/容器配置正确吗?
这六个问题,花两分钟逐项过一遍,比反复重启、改配置、然后怀疑人生有效得多。建一个自己的排查备忘录,遇到问题先查这个——90%的”服务正常但连不上”都能在这六个环节找到答案。
章末提问
Q1:服务端正常启动但客户端连不上,你的排查思路是什么?
结论先行:按「可达性链路」逐层排查:ping(IP 层)→ telnet/nc(端口层)→ 防火墙 → 监听地址 → SELinux → 代理/NAT。因为「连不上」90% 是网络可达性问题而非代码问题,任何一层拦截都会让请求到不了服务,逐层排除能用最小代价定位 90% 的故障。
Q2:telnet 报 Connection refused 和连接超时,分别说明什么问题?
结论先行:refused 说明端口没监听,超时说明包被中间设备丢弃(大概率防火墙)。因为 refused 是对方机器回了 RST——机器通但目标端口无人监听;超时是 SYN 发出去没回应——机器可能通但请求被防火墙静默丢弃,两者指向的排查方向完全不同。
Q3:为什么服务监听 127.0.0.1 时外部访问不到?
结论先行:因为回环地址只在本机内部有效,绑定它等于只对本机进程开放。因为外部请求到达网卡后,内核发现目的地址不是本机监听的任何对外地址,直接丢弃;所以对外服务必须监听 0.0.0.0(所有接口),这是很多框架默认绑 127.0.0.1 导致的经典坑。