
“ping 得通、端口连不上”这句话在服务器相关的技术群里出现的频率大概仅次于“服务挂了”。它最烦人的地方在于各方给出的排查结论互相矛盾网络组说链路没问题应用组说进程已经起来了云平台说安全组是放行的。三方都没说谎但业务就是不通。我这些年处理过的同类问题里真正因为网线、光模块这类物理故障导致的不到一成绝大多数卡在四五个具体位置上服务只监听了回环地址、操作系统防火墙悄悄拦了包、安全组只放通了某个源段、容器端口映射绑到了 127.0.0.1、以及客户端出口策略把出方向封死了。这篇文章把这类问题的排查方法完整拆开从“包到底走到了哪一层”开始一层层收敛到具体病灶包括命令行工具怎么选、返回结果怎么读、抓包如何一锤定音。适合正在被这类问题卡住的开发和运维也适合刚接手服务器、想建立一套排查肌肉记忆的新人。1. 先分清两种失败包被丢了还是包到了没人接1.1 ICMP 通了只代表 IP 这条路能走ping 用的是 ICMP 协议工作在网络层。它验证的事情很有限本机有到目标的路由、中间设备愿意转发、目标 IP 协议栈愿意回一个 Echo Reply。很多生产环境的主机或中间设备会限制 ICMP 频率甚至完全不回所以 ping 不通不等于服务不可用反过来ping 得通也只证明“到 IP 这一层是活的”跟 TCP 8080 端口上有没有进程在监听完全是两件事。打个比方ping 像往某栋楼打电话确认“这个楼号存在”测端口像去敲具体的房间门。楼在门不一定开也可能门开着但屋里没人应。另外一个容易被忽略的点是对象错位。你 ping 的地址可能根本不是你服务所在的地址。域名有多个 A 记录、VIP 后面挂多台后端、NAT 内外地址映射不同都会让 ping 的对象和真正承载服务的对象错开。这种错位会让两个工具给出完全相反的结论也是后面第 6 章要专门讲的场景。1.2 用一条命令区分 timeout 和 refused这是第一分水岭在任何排查动作之前先做一次端口探测然后看失败的类型而不是急着去翻防火墙规则connection refused连接被拒绝对端回了 RST。说明 SYN 包已经到达目标主机的协议栈目标主机的 IP 栈明确告诉你“这个端口没人在听”。责任大部分落在服务端——服务没起、只监听回环地址或者防火墙用了 REJECT 动作。connection timed out / no response什么都没回来或者中途被静默丢弃。可能是防火墙 DROP、安全组未放行、路由不通、上游有设备丢包。有时通有时不通指向 accept 队列溢出、conntrack 表满、后端多台机器状态不一致或者进程在崩溃循环里反复重启。这个“refused 还是 timeout”的判断基本决定后面几条命令该往哪个方向敲。跳过这一步直接怀疑防火墙是把简单问题做复杂的最常见路径。1.3 从客户端到应用进程拆成六段链路一次 TCP 连接要经过这样几段DNS 解析、客户端路由与出口策略、内网或互联网的中间设备、服务器网卡与安全组、操作系统防火墙、监听 socket 与应用 accept。任何一段断掉用户看到的现象都是“端口不通”但修复手段完全不同。我自己的检查习惯是双向往中间夹先从客户端往外确认甲方侧大概率没问题再从服务端往里确认服务在不在监听。两组结果一交叉矛盾点立刻暴露。比如客户端报 refused服务端 ss 却看不到监听那就是服务真的没起来客户端报 timeout服务端 ss 显示监听正常那多半是中间拦了包。这个交叉判断比任何单一工具都有效。2. 手里得有趁手的探针端口连通性测试工具怎么选2.1 telnet、nc 和 bash 内建 /dev/tcp 这三把最轻的刀telnet是最老牌的方式telnet 10.0.0.5 3306连上显示 Connected被拒立刻返回被丢弃就一直卡着。缺点是 Linux 发行版默认不一定装客户端包而且卡住时只能手动 CtrlC。ncnetcat更灵活nc -vz -w 3 10.0.0.5 3306。-z表示零 IO 只做探测-v输出详情-w 3设置超时。要注意 nc 有 GNU 和 OpenBSD 两个流派部分参数行为不完全一致老版本里的-w甚至只对建连之后的收发生效对 connect 阶段无效。所以稳妥的做法是外面再套一层 timeouttimeout 3 nc -vz 10.0.0.5 3306这个外层 timeout 是写批量探测脚本时最容易漏掉的一环。一旦目标被防火墙 DROP没有它脚本会一直挂在那里扫几十台机器能把整个任务卡死。容器里什么都没有时的兜底方案是 bash 内建的 /dev/tcptimeout 3 bash -c echo /dev/tcp/10.0.0.5/3306 echo open || echo closed它依赖编译时开启了 net redirections 的 bash用 shdash会报错。返回码 0 表示连接建立成功非 0 表示失败或者超时。2.2 curl 与 openssl s_client把应用层也一起测了端口通不等于服务健康。TCP 握手成功、进程却一个字节都不回是另一类故障用 curl 能测到更上层curl -sv --connect-timeout 5 http://10.0.0.5:8080/health-v输出里出现Connected to ... port 8080这一行说明 TCP 通了。如果之后请求一直没有响应那就不是端口问题而是应用层问题——线程池占满、GC 卡顿、后端依赖超时、连接池耗尽都有可能。HTTPS 场景用openssl s_client -connect app.example.com:443 -servername app.example.com能在握手阶段就看到证书链、TLS 版本、SNI 是否正确传递。TLS 握手失败被误报成“端口不通”的情况非常普遍实际上 TCP 早就通了只是应用层协议协商没通过。2.3 Windows 与 PowerShell 下的等价写法Windows 自带 telnet 客户端默认没启用需要手动开启功能。PowerShell 里更顺手的是Test-NetConnection -ComputerName 10.0.0.5 -Port 8080关注输出里的TcpTestSucceeded字段。它本质上就是发起一次 TCP 连接不依赖 ping所以目标禁 ICMP 的环境里同样能用。查本机端口占用用netstat -ano | findstr :8080拿到 PID 后再用Get-Process -Id pid反查进程。Windows 防火墙规则用Get-NetFirewallRule、New-NetFirewallRule管理入站默认是“未匹配即阻止”的逻辑跟 Linux 上默认策略 DROP 是一个思路。2.4 nmap 的 --reason 才是精髓但使用边界要守住nmap -Pn -p 22,80,443,8080 --reason 10.0.0.5-Pn跳过主机发现目标禁 ICMP 也能测--reason会把每个端口的结果原因打出来syn-ack表示端口开着reset连接被拒表示端口关着但对端主机活着no-response表示疑似被过滤或静默丢弃。这三者的区分比单纯的 open/closed 有用得多直接对应前面说的 refused 与 timeout 分水岭。注意端口扫描工具只在自己负责的服务器或已获得明确授权的资产上使用。对内网做资产梳理之前最好先和网络负责人同步一下避免被内网安全设备判定为异常扫描源而把源 IP 封掉反而影响排查。3. 服务端自查九成问题出在“到底监听了谁”3.1 ss -lntp 里 127.0.0.1 和 0.0.0.0 的差别是决定性的登录服务器第一件事ss -lntp | grep 8080看 Local Address 那一列127.0.0.1:8080、0.0.0.0:8080、*:8080、[::]:8080含义完全不同。绑定 127.0.0.1 意味着这个 socket 只接受本机回环发来的连接从别的机器过来的包无论防火墙怎么放通内核都不会交给这个服务现象上通常表现为连接被拒或者超时。这是新手最常见的坑也是老手偶尔翻车的点。框架默认配置就爱这么写Spring Boot 的server.address、Node 的app.listen(8080, 127.0.0.1)、MySQL 的bind-address 127.0.0.1、Redis 的bind 127.0.0.1 -::1都是一样的道理。改配置绑定到 0.0.0.0 或指定业务网卡地址后重启服务即可。有一点必须提醒把服务暴露到 0.0.0.0 之前先确认认证和访问控制已经做到位。数据库、缓存这类不该直接面对公网的服务正确做法是用安全组把源限制在内网段而不是简单粗暴地全网开放。3.2 IPv6 双栈带来的假象ss -lnt里看到:::8080表示监听在所有 IPv6 地址上。在net.ipv6.bindv6only0多数发行版默认值的情况下它同时也会接受 IPv4 连接内核会把 IPv4 地址映射成::ffff:10.0.0.5。但如果这个参数被设成 1只监听::就真的收不到 IPv4 的包了客户端体验就是超时或者拒绝。还有些程序会分别绑定0.0.0.0:80和[::]:80两个 socket另一些只绑其中一个行为并不统一。排查时建议把两个地址族分开各测一遍用nc -4和nc -6显式指定避免一个通了另一个没通却被当成整体正常。3.3 日志说了 Startedss 里却找不到端口这种情况通常有四个来源。一是进程启动后绑定端口失败但异常被 try/catch 吞掉了进程还活着却根本没在监听日志里搜bind、EADDRINUSE、Address already in use往往能找到线索。二是端口被另一个老进程抢占lsof -i:8080查到 PID 后用ps -fp pid确认是不是上次没杀干净的实例。三是服务跑在容器里宿主机上的 ss 当然看不到对应监听要进容器查。四是 systemd 的服务用了 socket activation端口由 systemd 持有ss 里显示的进程是 systemd 而不是业务程序此时看日志和systemctl status更直接。3.4 端口权限和 SELinux 这类隐藏限制Linux 上绑定 1024 以下的端口需要 root 权限或 CAP_NET_BIND_SERVICE 能力非 root 程序会直接报错退出日志里是Permission denied如果只看“服务没起来”很容易走偏方向。更隐蔽的是 SELinux。现象很典型端口能起本机 curl 也通外网访问就是不行。getenforce确认是不是 Enforcingsemanage port -l | grep http_port_t查看该类服务允许使用的端口清单把新端口加进去semanage port -a -t http_port_t -p tcp 8081这个坑在 CentOS、RHEL 以及部分国产发行版上都很常见。改完记得重启服务或执行restorecon光加规则不重启也不生效。4. 防火墙与安全组四个拦截点逐一过4.1 firewalld、iptables、ufw 的实际检查姿势firewalld 是现在最常见的firewall-cmd --state firewall-cmd --get-active-zones firewall-cmd --list-all firewall-cmd --add-port8080/tcp --permanent firewall-cmd --reload这里有两个经典坑点--add-port不带--permanent只在运行时生效重启后规则丢失带了--permanent不执行--reload又不生效。两个条件必须同时满足规则才是持久有效的。iptables 和 nftables 场景看iptables -L -n -v --line-numbers注意观察每行前面的包计数能看出规则有没有被命中。重点核对 INPUT 链的默认策略和 DROP/REJECT 规则。DROP 表现为超时REJECT 表现为连接被拒这正好是前面那个分水岭的由来。ufw 用得少一些ufw status verbose看状态ufw allow 8080/tcp放行规则顺序同样要注意。注意Docker 会在 nat 表和 filter 表里插入自己的链DOCKER、DOCKER-USER。你在 INPUT 链上加的规则对已经发布到宿主机的容器端口可能完全不起作用因为那部分流量走的是 FORWARD 链。要限制容器端口规则得写到 DOCKER-USER 链里。4.2 云服务器上的安全组与子网 ACL云环境里往往有两层虚拟防火墙安全组绑定实例网络 ACL 绑定子网。安全组一般是有状态的出方向默认全放入方向默认全禁。检查三件事协议和端口写对没有、源 IP 段是否覆盖客户端出口、规则是否真的绑到了目标实例上。改完规则通常立即生效但如果客户端侧还挂着旧的长连接要断开重连才会走新规则容易被误判成“改了没用”。另外云内网实例之间互访也要确认规则里允许了对方的内网网段很多故障就是公网规则配了、内网规则忘了。4.3 Docker 与容器端口映射的两个陷阱docker port container docker inspect -f {{.NetworkSettings.Ports}} container第一个陷阱是-p 127.0.0.1:8080:80这种写法只把端口发布到宿主机回环地址外部访问自然不通改成-p 8080:80才是绑到所有地址。第二个陷阱是容器内服务本身只监听了 127.0.0.1这时候光改宿主机的映射也救不回来因为代理组件转发到容器内部回环时源地址会变成网关地址某些应用会对源地址做校验并拒绝。Kubernetes 环境里链路更长对应的是 Pod 内服务的监听地址、Service 的 targetPort、NodePort 的端口范围、代理组件的规则、网络插件的策略。排查顺序和单机一致只是每个环节都需要单独确认别指望一步到位。4.4 客户端出口策略和你自己的那一段网络如果内网访问正常只有某些办公网点或跨区域节点不通大概率问题出在客户端侧的出方向。企业出口设备经常按目的端口做白名单只放行常见端口反过来公司内网也可能禁止访问非标端口。这种情况在服务器上抓包会看到 SYN 根本没到达而客户端本地状态是“一直卡着”。把这个判断做出来责任边界就清楚了如果是出口策略服务器怎么改都没用如果不是就该回到服务端继续查。很多团队卡在反复改服务器配置上就是因为没做这一步客户端侧的验证。5. 抓包定论一次 tcpdump 收敛所有猜测5.1 四种抓包结果对应四种病因tcpdump -i any -nn -c 20 host 203.0.113.10 and tcp port 8080抓包前先想清楚要验证什么再看结果。客户端侧看到的三次握手情况可以这样解读抓到的包说明病因指向只有 SYN反复重传包发出去但没回应中间 DROP、安全组未放行、路由黑洞SYN 之后收到 RST对端明确拒绝服务没监听、防火墙 REJECT、端口关闭三次握手完成但应用无响应TCP 通了业务层卡住应用未 accept、后端依赖超时、线程池满连 SYN 都发不出去本地路由或出口策略失败本机路由表、客户端防火墙、出口设备服务端侧抓包的价值在于确认 SYN 是否真的到达了网卡。如果本机 tcpdump 能看到 SYN但客户端收不到 SYN-ACK问题在服务端出口方向或回包路径如果本机 tcpdump 也看不到 SYN说明包在到达网卡之前就被丢掉了责任在上游安全组或路由。两端同时抓一次比反复猜测高效得多。5.2 accept 队列溢出与 conntrack 表满“时通时不通”最常见的两个原因。一是 accept 队列溢出。ss -lnt输出的 LISTEN 行里Recv-Q 表示已经完成三次握手、等待应用 accept 的连接数Send-Q 是 backlog 上限。Recv-Q 长期贴近 Send-Q就说明应用处理速度跟不上连接建立速度。用nstat -az TcpExtListenOverflows或netstat -s | grep -i listen能看到累计溢出次数这个数字持续增长基本可以定案。二是连接跟踪表满。dmesg | grep -i conntrack里如果出现nf_conntrack: table full, dropping packet那就是原因。表现是新连接随机失败、老连接照常工作非常有迷惑性。临时办法是调大net.netfilter.nf_conntrack_max长期办法是优化规则减少不必要的连接跟踪比如把内部互访的高频流量排除在跟踪之外。5.3 客户端本地端口耗尽与文件描述符上限如果客户端报Cannot assign requested address多半是 TIME_WAIT 积累太多把本地临时端口用光了。ss -s看一下 timewait 数量net.ipv4.ip_local_port_range决定可用范围。这是纯粹客户端侧的问题服务端抓包会看到一条请求都没有。另一类是文件描述符不够ulimit -n看一下上限值高并发服务上这个值偏低会表现为“能连上但很快断开”。这两类问题都不在服务器和网络之间而在客户端自己的资源上排查时容易完全忽略。6. 几个容易误导人的场景实录6.1 域名解析到多台机器ping 通的是“旧”那台多 A 记录或者 DNS 轮询的场景下ping 和测端口可能落在不同机器上得到完全相反的结论。诊断方法是先把域名解析成 IPdig short app.example.com然后对每一个 IP 单独测端口。如果发现只有某一个 IP 不通问题就锁定在那台机器上——可能是扩容时漏配了服务、老机器没下线、或者某个节点的进程挂了。这类问题在 K 8 s 集群的外部域名上尤其常见因为一个域名背后可能是负载均衡的多个地址而某些地址指向的节点状态不一致。6.2 中间设备代答 ICMPping 的结论完全失效有些防火墙配置成代答 ICMP即使后端已经全挂ping 依然是通的。VIP 场景同理ping 通 VIP 只说明这个虚拟地址存在不代表后面挂着任何健康的后端。这类环境里 ping 的参考价值接近于零必须直接测 TCP 端口而且要连续测多轮比如用一段循环跑十次观察成功率。偶发失败和持续失败的处理思路完全不同前者查队列和负载后者查规则和监听。6.3 时间不同步被误判成端口不通时间服务器没同步、宿主机和虚拟机之间时钟漂移会导致 TLS 证书校验失败——证书看起来还没生效或者已经过期。现象是客户端连 HTTPS 端口时握手失败很容易被判断成“端口有问题”实际上 TCP 早就通了。这种情况在虚拟化环境里尤其多虚拟机挂起再恢复后时间会跳。检查方法很轻量timedatectl看同步状态chronyc sources看时间源是否正常。排查端口问题时顺手看一眼时钟成本几乎为零但省下的时间可能是一整个下午。6.4 只在特定时间不通规律本身就是线索有些服务会被定时任务重启或者伴随日终批处理把资源耗尽形成“白天好、凌晨挂”的规律。还有一种是用 restart 策略加崩溃循环端口在开与关之间反复切换探测时随机得到两种结果。遇到这类现象先看服务的重启次数systemctl show svc -p NRestarts再配合journalctl拉时间线看崩溃时间点和什么任务重合。规律本身就是最重要的线索比逐条检查配置快得多。7. 把它固化成流程和脚本7.1 一张从外到内的排查顺序表步骤动作观察点1客户端 nc 或 Test-NetConnection 测端口被拒还是超时2解析域名对所有 IP 分别测是否单点异常3服务器 ss -lntp 看监听地址127.0.0.1 还是 0.0.0.04服务器本机 curl 回环地址区分服务本身和网络问题5检查 firewalld / iptables / ufw规则与默认策略6检查安全组与子网 ACL源段与端口7两端 tcpdump定位丢包发生的具体位置8查队列、conntrack、时钟解释偶发和“诡异”现象顺序不必死板照搬但第一步一定要先测出 refused 还是 timeout。有了这个分类后面每一步的取舍都有依据。7.2 一段可复用的巡检脚本#!/usr/bin/env bash # 用法: ./check_port.sh host port [timeout秒] host$1; port$2; t${3:-3} if timeout $t bash -c echo /dev/tcp/$host/$port 2/dev/null; then echo [OK] $host:$port 可连接 else echo [FAIL] $host:$port 不可连接端口未监听或包被丢弃 fi配合watch -n 5或者写进定时任务做长期探测出现偶发问题时手里就有历史记录不用靠回忆去猜“刚才到底通不通”。这套东西在排查“时通时不通”时价值极高。7.3 上线前的端口自检清单服务上生产之前我自己固定过一遍这几条监听地址是否为 0.0.0.0 或明确的业务网卡地址、端口是否在安全组放行、系统防火墙是否放行、SELinux 是否允许该端口、非 root 启动时是否用了 1024 以上端口、容器映射是否绑到了所有地址、健康检查路径是否真的返回 200、从另一台机器实测一次而不是只在本机 curl 回环。这八条花不了十分钟能省掉后面几小时的来回扯皮。尤其是最后一条本机 curl 通不等于外部能访问这个认知差是绝大多数同类问题的根源。最后分享一个习惯每次解决完这类问题把当时的现象、用到的命令、最终结论记一条到自己的排查笔记里格式随意比如“现象外网 8443 超时本机 curl 正常结论安全组只放了 443修复补规则”。攒上半年你会发现这类问题的模式就那么几种定位时间能从几小时压到几分钟因为手上已经有了一套验证过的路径而不是每次从零开始猜。