端口已监听但连接仍超时:按四层路径排查 Linux 网络

43 次浏览3 条回复

适用于 Linux 服务端显示端口已监听,但远端客户端仍出现连接超时、拒绝连接或 TLS 握手失败的场景。命令基线为 iproute2 5.5+、systemd 245+、curl 7.68+;tcpdump 4.9+ 和 nft 0.9+ 为可选工具。示例端口为 8443、服务单元为 app.service,执行前必须替换为实际值。涉及抓包、规则和网络命名空间的命令通常需要 root 权限。

1. 先固定观察点并区分错误类型

在原始客户端记录时间、解析结果和连接过程:

date -u
getent ahosts api.example.com
curl -v --connect-timeout 5 https://api.example.com:8443/healthz

Connection refused 通常表示目标返回了 RST,优先检查监听地址和显式拒绝规则;超时表示在限定时间内没有完成连接,重点检查丢包、路由和静默丢弃规则;若已经收到 TLS 或 HTTP 错误,TCP 通路通常已建立,应转向证书、SNI、协议和应用日志。先保存完整错误,不要只记录“访问不了”。

2. 在服务端确认监听者与绑定地址

date -u
sudo ss -lntp 'sport = :8443'
systemctl show app.service -p ActiveState -p SubState -p MainPID
systemctl status app.service --no-pager
journalctl -u app.service --since '-10 min' --no-pager

监听在 127.0.0.1:8443[::1]:8443 时,远端无法直接连接;监听在 0.0.0.0:8443[::]:8443 才可能接收对应地址族的外部流量。ss 没有结果时,不要先改防火墙,应先确认进程是否启动、实际端口是否变化。

3. 从服务端本机验证应用层

若服务使用 HTTPS,使用真实主机名保留 Host 与 SNI,只把连接地址定向到本机:

curl -v --connect-timeout 3 \
  --resolve api.example.com:8443:127.0.0.1 \
  https://api.example.com:8443/healthz

不要用 -k 掩盖证书问题。若本机请求也失败,先处理应用、协议或监听问题;本机成功而远端失败,才继续检查主机外部链路。

4. 对照地址、回程路由与主机规则

ip -br address
ip route get CLIENT_IP
sudo nft list ruleset

CLIENT_IP 替换为原始客户端地址。ip route get 用于确认回包将从哪个接口、源地址和下一跳发出。只有系统实际使用 nftables 时才按 nft 输出判断;若主机由 firewalld、ufw、云安全组或外部负载均衡器管理,还要在对应控制面核对,但不要为了验证而清空整套规则。

5. 用少量抓包确定断点

在服务端发起一次客户端请求,同时执行:

sudo tcpdump -ni any 'tcp port 8443 and host CLIENT_IP' -c 20

判读顺序:服务端看不到 SYN,问题位于到达主机之前或客户端使用了错误地址;看到 SYN 但没有发出 SYN-ACK,检查本机丢弃规则、监听地址和资源状态;发出 SYN-ACK 却收不到 ACK,检查回程路由、非对称路径及客户端侧策略;三次握手完成后才失败,则继续看 TLS 和应用日志。抓包可能包含地址等敏感元数据,应限制过滤条件和包数。

6. 排除网络命名空间差异

容器或隔离服务中的 127.0.0.1 不等于宿主机。若 systemd 的 MainPID 大于 0,可只读比较并进入该进程的网络命名空间检查:

PID=$(systemctl show app.service -p MainPID --value)
readlink /proc/1/ns/net /proc/"$PID"/ns/net
sudo nsenter -t "$PID" -n ss -lntp 'sport = :8443'

nsenter 来自 util-linux;执行前先确认 PID 属于目标服务。若两个 net:[...] 标识不同,宿主机与服务看到的接口、路由和监听套接字不是同一组,需要再检查端口发布、代理或容器网络配置。

7. 闭环验证

每次只改变一个可解释的配置项。处理后至少重新确认:

sudo ss -lntp 'sport = :8443'
systemctl is-active app.service
journalctl -u app.service --since '-2 min' --no-pager

最后必须从最初失败的客户端再次执行同一条 curl,记录 UTC 时间、解析到的地址、连接结果和服务端对应日志。仅本机成功不能证明远端链路已经恢复。

补一个双栈场景的检查点:[::]:8443 不一定同时接收 IPv4,结果受应用是否设置 IPV6_V6ONLY 以及 net.ipv6.bindv6only 影响;客户端解析到 A、AAAA 后,也可能只有其中一条路径故障。可在原始客户端分别强制地址族:

getent ahosts api.example.com
curl -4 -v --connect-timeout 5 https://api.example.com:8443/healthz
curl -6 -v --connect-timeout 5 https://api.example.com:8443/healthz

服务端按地址族核对监听,并只读查看内核设置:

sudo ss -4lntp 'sport = :8443'
sudo ss -6lntp 'sport = :8443'
sysctl net.ipv6.bindv6only

命令前提仍是主题所列的 iproute2、curl 与 Linux sysctlcurl -6 还要求客户端具备可用的 IPv6 路由。若只在一个地址族失败,应继续针对该地址族检查安全策略、路由和抓包,不要把强制 -4 当作最终修复。变更后仍应去掉 -4/-6,用原始主机名重新请求,确认默认解析顺序下也能成功。

再补一个资源饱和分支:端口处于 LISTEN 并不代表内核仍能正常接收新连接。若故障只在流量高峰出现,可在复现前后分别采样累计计数:

date -u
ss -lnt 'sport = :8443'
nstat -az TcpExtListenOverflows TcpExtListenDrops TcpExtTCPReqQFullDrop
sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog net.ipv4.tcp_syncookies

前提是 Linux 与 iproute2 的 ssnstat;不同内核未必提供全部 TcpExt 指标,缺失项不能按零处理。对监听套接字,Recv-Q 可辅助判断等待应用 accept() 的已完成连接是否堆积,Send-Q 表示配置的 backlog 上限,但单次快照不足以证明溢出。应保留第一次输出,触发一次受控请求后再次执行同组命令,比较 ListenOverflowsListenDropsTCPReqQFullDrop 是否增长,并同时对齐应用日志与 CPU、文件描述符状态。

若主机启用了 netfilter 连接跟踪,还可只读核对容量:

sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max

该节点不存在通常表示模块未加载或当前环境未暴露该接口,不应据此判定异常。不要直接放大 backlog、关闭连接跟踪或改同步机制;先用计数增量确认瓶颈,再检查应用是否及时 accept()、工作线程是否阻塞以及进程文件描述符上限。调整后仍要从原始客户端复测,并确认上述丢弃计数不再随请求增长。

再补一个容易被误判为应用故障的分支:路径 MTU 黑洞。小型 SYN/ACK 可以完成三次握手,但 TLS 或 HTTP 开始传输较大报文后持续重传;这在 VPN、隧道、跨云链路或 ICMP Fragmentation Needed / Packet Too Big 被过滤时更常见。可在复现窗口从服务端限量执行 ip route get CLIENT_IPss -tin 'sport = :8443',以及 sudo tcpdump -ni any -vv '((tcp port 8443) and host CLIENT_IP) or icmp or icmp6' -c 100。前提为主题所列的 iproute2 5.5+、tcpdump 4.9+,抓包需 root 权限;将占位地址替换为实际值,并注意输出可能包含地址、端口和 TCP 元数据。若同一段较大 TCP 序列反复重传、收不到对应 ACK,同时出现 ICMP MTU 通知,PMTU 问题的证据较强;只有单次重传不能下结论。客户端若安装了 iputils 的 tracepath,还可执行 tracepath -n SERVER_IP;用 ping -4 -M do -s 1472 -c 3 SERVER_IP 时,1472 只用于验证 1500 字节 IPv4 MTU 这一基线,应按实际链路逐步调整,且 ICMP Echo 被禁时失败结果并不等于 MTU 异常。不要一开始就全局降低 MTU 或盲目添加 MSS 规则;优先恢复必要的 ICMP MTU 通知、修正隧道两端 MTU,确需 MSS 调整时只在明确的边界接口和流量方向上变更。最后用原始客户端的同一条 curl 复测,并确认抓包中不再出现相同序列段持续重传。