能 ping 通服务却连不上:一次 Linux 网络分层排查复盘

机器 A 连机器 B 的服务连接失败,可 ping B 通、延迟 0.2ms,网络看似毫无问题。排查梳理:ping 走 ICMP 只能证明 L3 可达、连服务是 L4 的事;一次连接要从 IP 层到端口层到应用层一层层成立;telnet 的 refused 与 timeout 是两种病;ss -lnt 看监听地址、服务绑 127.0.0.1 外面够不着、自测别用 localhost;firewalld 与云安全组两层防火墙,以及一套网络分层排查纪律。

2024 年,我把一个新服务部署到机器 B 上,让机器 A 去调用它。结果 A 这边连接直接失败。我的第一反应,是先确认两台机器网络通不通——于是在 A 上 ping 了一下 B,几个回包干净利落地回来了,延迟 0.2 毫秒,漂亮得很。网络明明是通的,可服务就是连不上。这个矛盾把我卡了很久:ping 都通了,还能有什么网络问题?我下意识地认定"网络没问题",转头一头扎进了服务本身的代码和配置里,翻了很久,什么也没翻出来。真正让我跳出僵局的,是一个迟来的醒悟:ping 通,和"服务连得上",根本就不是同一件事——它们测的,是网络里完全不同的两层。我一直把"能 ping 通"当成"网络一切正常"的总开关,可它其实只点亮了那么一小段。这件事逼着我把 Linux 的网络分层、ping/telnet/ss、路由与防火墙这一整套彻底理清了。本文复盘这次实战。

问题背景

环境:CentOS 7,机器 A(10.0.0.21)调机器 B(10.0.0.31)的 8080
事故现象:
- A 连 B 的 8080 服务,连接失败
- 在 A 上 ping B,通,延迟 0.2ms,毫无问题
- "网络是通的",于是一头扎进服务配置,查了半天没结果

现场排查:
# 1. A 上 ping B —— 通
$ ping -c3 10.0.0.31
64 bytes from 10.0.0.31: icmp_seq=1 ttl=64 time=0.21 ms   # ★ 通

# 2. ★ 但是 telnet 那个端口 —— 不通
$ telnet 10.0.0.31 8080
Trying 10.0.0.31...
(长时间卡住,最后 Connection timed out)                  # ★ 端口不通!

# 3. ★ 跑到 B 上,看 8080 到底有没有在监听
$ ss -lnt | grep 8080
LISTEN  0  128  127.0.0.1:8080  *:*       # ★ 监听在 127.0.0.1!
#                ^^^^^^^^^ 只绑了本地回环地址

# 4. B 上的防火墙也顺手看一眼
$ firewall-cmd --list-ports
(8080/tcp 不在里面)                       # ★ 防火墙也没放行

根因(后来想清楚的):
1. ★ ping 通,只能说明 A 和 B 之间【第三层(IP 层)】
   是通的 —— 数据包能在两台机器间路由往返。
2. ★ 但"连上一个服务"是【第四层(TCP)】的事,
   它还要求:目标端口有进程在监听 + 防火墙放行 +
   服务绑的地址是对的。ping 一个都管不着。
3. 真相一:B 的服务绑在 127.0.0.1:8080 —— 只有 B
   自己能连,外面的 A 根本够不着。
4. 真相二:就算改成绑 0.0.0.0,B 的 firewalld 也
   没放行 8080,A 的 TCP 握手包会被防火墙丢掉。
5. ★ 我把"ping 通"误当成"网络全通",于是跳过了
   端口、绑定地址、防火墙这三个真正出问题的环节。
ping 通只代表 L3 通,服务连不上要一层层往上测。

修复 1:一次连接要穿过几层——ping 通只点亮了一段

# === ★ "A 连上 B 的服务",不是一件事,是【一串】事 ===
# 它要从下到上,一层层都成立:

# 第 ① 层 - 网线/网卡通:物理链路、网卡 UP
# 第 ② 层 - IP 可达:A 的包能路由到 B,B 的包能回来
#           ★ ping 测的就是到这一层为止
# 第 ③ 层 - 端口可达:B 上 8080 端口的 TCP 握手能完成
# 第 ④ 层 - 服务就绪:8080 后面真有个进程在正常处理

# === ★ 关键认知:ping 通 = 第 ② 层通,仅此而已 ===
# ping 走的是 ICMP 协议,它验证的是"IP 包能不能往返"。
# 它【完全不碰】端口,不碰 TCP,不碰具体某个服务。
# ★ 所以"ping 通"能告诉你的,只有一句话:
#   "A 和 B 之间,IP 层的路是通的。"
# 它【不能】告诉你:8080 有没有人听、防火墙放没放行、
#   服务进程是死是活 —— 这些全在它的视野之外。

# === ★ 正确的排查姿势:从下往上,一层层确认 ===
$ ping B_IP                       # ② IP 层通不通
$ telnet B_IP 端口                # ③ 端口通不通(TCP 握手)
$ curl http://B_IP:端口/          # ④ 服务应用层正不正常
# ★ 在哪一层第一次失败,问题就在那一层 —— 别越级。
# 我这次的错,就是②通了就直接跳到④,漏掉了③。

# === 一个形象的比喻 ===
# ping 通 = "这栋楼的地址是真的、路能开到楼下"。
# 但你要找的那个房间(端口)有没有人、
#   门卫(防火墙)放不放你进 —— ping 一概不知道。

修复 2:ping 到底测了什么——ICMP 与 L3 可达性

# === ★ 把 ping 这个工具本身看清楚,才不会高估它 ===

# === ping 用的是 ICMP 协议,不是 TCP/UDP ===
$ ping -c4 10.0.0.31
64 bytes from 10.0.0.31: icmp_seq=1 ttl=64 time=0.21 ms
#                        ^^^^^^^^^^ ★ ICMP,跟"端口"毫无关系
# ping 发 ICMP echo request,对方回 ICMP echo reply。
# 整个过程【没有端口的概念】—— 这就是它只能测 L3 的原因。

# === ping 的几个有用参数 ===
$ ping -c4 IP                     # -c4 发 4 个就停(别无限刷)
$ ping -W1 IP                     # -W1 单个回包最多等 1 秒
$ ping -s 1400 IP                 # -s 指定包大小(可测 MTU 问题)

# === ★ 一个大坑:ping 不通 ≠ 网络不通 ===
# 很多服务器、防火墙会【主动屏蔽 ICMP】(安全策略)。
# ★ 所以 ping 不通,也可能只是"对方不理 ICMP",
#   而 TCP 服务其实好好的。
# 反过来,ping 通也不代表 TCP 能通。
# ★ 结论:ping 只能【单向证明】L3 通;它的"不通",
#   和服务的"通不通",都不能直接划等号。

# === ttl 字段能看出"中间过了几跳" ===
# 回包的 ttl,从一个初始值(64/128/255)每过一个
#   路由器减 1。ttl=64 基本是同网段直连;
#   ttl 明显变小 -> 中间过了好几跳路由。

# === 看 IP 层路由通不通,还能用 traceroute ===
$ traceroute 10.0.0.31            # 列出到对方经过的每一跳
$ mtr 10.0.0.31                   # ★ traceroute + ping 的合体,
                                  #   能看每一跳的丢包率,更强
# ★ 这些都还在 L3 这一层 —— 它们解决"路通不通",
#   解决不了"端口通不通"。

修复 3:端口通不通——telnet/nc 测 L4,ss 看监听

# === ★ ping 之后必做的一步:测【端口】通不通 ===

# === 在客户端 A 上:telnet 测 TCP 端口 ===
$ telnet 10.0.0.31 8080
Trying 10.0.0.31...
Connected to 10.0.0.31.              # ★ 出现 Connected = 端口通!
# - Connected             -> TCP 握手成功,端口是通的
# - Connection refused    -> ★ 包到了,但没人监听这个端口
# - Connection timed out  -> ★ 包石沉大海(防火墙丢包/路由不到)
# ★ refused 和 timeout 是两种【完全不同】的故障,要分清。

# === 更专业的工具:nc(netcat)===
$ nc -zv 10.0.0.31 8080              # -z 只测不发数据 -v 显示结果
Ncat: Connected to 10.0.0.31:8080.   # 通
$ nc -zv 10.0.0.31 8000-8100         # 还能扫一个端口范围

# === ★ 在服务端 B 上:ss 看端口到底有没有在监听 ===
$ ss -lnt
State   Recv-Q  Send-Q  Local Address:Port
LISTEN  0       128     127.0.0.1:8080            # ★ 看这一行
LISTEN  0       128     0.0.0.0:22
# -l 只看 LISTEN 的  -n 不解析名字  -t 只看 TCP
# ★ 如果 grep 8080 啥都没有 -> 服务根本没起来/没监听这个口。

# === 看某端口是被哪个进程监听的 ===
$ ss -lntp | grep 8080               # -p 显示进程
LISTEN ... 127.0.0.1:8080  users:(("java",pid=9001,fd=5))

# === ★ 把 refused / timeout 对应到原因 ===
# telnet 报 refused:
#   -> 端口没进程监听,或服务挂了 -> 上 B 用 ss -lnt 查
# telnet 报 timed out:
#   -> 包被防火墙丢了,或绑定地址不对 -> 查防火墙 + 绑定地址
# ★ 这次我 telnet 报的是 timed out,顺着它,
#   果然挖出了"绑定 127.0.0.1"和"防火墙没放行"两个根因。

修复 4:服务绑在哪个地址——127.0.0.1 的经典坑

# === ★ 这次根因之一:服务绑在了 127.0.0.1,外面够不着 ===

# === ss 输出里那个 "Local Address" 是关键 ===
$ ss -lnt | grep 8080
LISTEN 0 128 127.0.0.1:8080    *:*     # ★ 绑 127.0.0.1
LISTEN 0 128 0.0.0.0:8080      *:*     # ★ 绑 0.0.0.0(对比)

# === 127.0.0.1 和 0.0.0.0 的天壤之别 ===
# 监听 127.0.0.1:8080
#   -> 只有【本机】能连这个服务。
#   -> 别的机器(比如 A)来连,根本到不了 —— 内核直接不睬。
#   -> ★ 在 B 本机 curl 127.0.0.1:8080 是通的,
#         这会给你"服务正常"的【错觉】!
# 监听 0.0.0.0:8080
#   -> 本机【所有网卡/所有 IP】上都能被连。
#   -> 这才是"想让别的机器连进来"该用的。

# === ★ 这个坑最迷惑人的地方 ===
# 你在 B 上自测:curl localhost:8080 -> 通!服务没问题啊!
# 可 A 上死活连不上。
# ★ 因为你自测走的是 127.0.0.1,而 A 走的是 10.0.0.31 ——
#   服务只在 127.0.0.1 上听,自测当然通,A 当然不通。
# 排查"别人连不上"时,自测一定要用【对外 IP】:
$ curl http://10.0.0.31:8080/         # 用对外 IP 自测,别用 localhost

# === 怎么改:让服务绑 0.0.0.0 ===
# 这是【应用配置】的事,各家不同,常见的:
#   nginx     listen 8080;            (默认就是所有地址)
#   Spring    server.address=0.0.0.0
#   通用       配置里把 bind/host 从 127.0.0.1 改成 0.0.0.0
# 改完重启服务,再 ss -lnt 确认变成了 0.0.0.0:8080。

# === ★ 安全提醒:0.0.0.0 是"对所有人开门" ===
# 绑 0.0.0.0 后,这个端口对能路由到这台机的人都可见,
#   务必靠【防火墙】来限定"只允许哪些来源 IP" —— 见下节。

修复 5:防火墙这道暗门——firewalld 与 iptables

# === ★ 这次根因之二:B 的防火墙没放行 8080 ===
# 服务监听对了,防火墙这关没过,A 的握手包照样被丢。

# === CentOS 7 默认是 firewalld ===
$ systemctl status firewalld          # 看防火墙开没开
$ firewall-cmd --state                # running / not running

# === ★ 看当前放行了哪些端口/服务 ===
$ firewall-cmd --list-all
  services: ssh dhcpv6-client
  ports:                              # ★ ports 是空的 -> 8080 没放行
# 8080 既不在 services 也不在 ports 里 -> 它是被挡着的。

# === ★ 放行一个端口(持久)===
$ firewall-cmd --add-port=8080/tcp --permanent   # --permanent 持久
$ firewall-cmd --reload                          # ★ 重载才生效
$ firewall-cmd --list-ports                      # 确认 8080/tcp 在了

# === ★ 更安全:只放行给特定来源 IP ===
$ firewall-cmd --permanent --add-rich-rule=\
  'rule family=ipv4 source address=10.0.0.21 port port=8080 protocol=tcp accept'
$ firewall-cmd --reload
# 这样只有 A(10.0.0.21)能连 8080,别的来源照样挡 ——
#   比 --add-port 对全世界开放安全得多。

# === 如果系统用的是 iptables ===
$ iptables -L -n --line-numbers       # 看现有规则
$ iptables -nvL INPUT                 # 看 INPUT 链,关注 DROP/REJECT

# === ★ 别忘了云服务器还有一层"安全组" ===
# 云主机(阿里云/腾讯云等)在【机器之外】还有一层
#   "安全组"防火墙,在云控制台配。
# ★ 经典坑:系统内 firewalld 放行了,云安全组没放行 ->
#   还是 timeout。排查云上的端口不通,这一层必查。

# === 怎么确认"就是防火墙在丢包" ===
# 在 A telnet B 的同时,在 B 上抓包:
$ tcpdump -i any -nn tcp port 8080
# ★ 抓到了 A 的 SYN 包、但 B 没回 -> 包到了内核被防火墙丢了。
#   连 SYN 都没抓到 -> 包在更外层(云安全组/路由)就没了。

修复 6:网络分层排查纪律

# === 这次事故暴露的认知盲区,定几条纪律 ===

# === 1. ★ ping 通只代表 L3 通,绝不等于"服务能连" ===
# ping 走 ICMP,不碰端口、不碰 TCP、不碰具体服务。

# === 2. ★ 从下往上分层测,在哪层失败问题就在哪层 ===
$ ping B_IP                # ② IP 层
$ telnet B_IP 端口         # ③ 端口/TCP 层
$ curl http://B_IP:端口/   # ④ 应用层

# === 3. ★ telnet 的 refused 和 timeout 是两种病 ===
# refused = 端口没人监听  -> 上服务端 ss -lnt 查
# timeout = 包被丢了      -> 查防火墙、查绑定地址

# === 4. ★ 服务端必查:监听地址是不是 127.0.0.1 ===
$ ss -lntp | grep 端口     # 绑 127.0.0.1 外面连不上,要绑 0.0.0.0

# === 5. ★ 自测要用对外 IP,别用 localhost ===
$ curl http://对外IP:端口/  # 用 localhost 会给你"服务正常"的错觉

# === 6. 防火墙:系统 firewalld + 云安全组,两层都要查 ===
$ firewall-cmd --list-all  # 系统这层
# 云主机还要去控制台看安全组那层。

# === 7. 排查"连不上"的命令链 ===
$ ping B_IP                        # ① L3 通不通
$ telnet B_IP 端口 / nc -zv         # ② 端口通不通
$ ss -lntp | grep 端口             # ③(服务端)有没有监听、绑哪
$ firewall-cmd --list-all          # ④(服务端)防火墙放行没
$ tcpdump -nn tcp port 端口        # ⑤ 抓包看 SYN 到没到/有没有回
# 按这个顺序,"连不上"基本能定位到层。

命令速查

需求                        命令
=============================================================
测 IP 层(L3)可达           ping -c4 IP
看到对方的每一跳路由        traceroute IP  /  mtr IP
测某个 TCP 端口通不通       telnet IP 端口  /  nc -zv IP 端口
看本机监听了哪些端口        ss -lnt
看端口被哪个进程监听        ss -lntp | grep 端口
看防火墙放行了什么          firewall-cmd --list-all
放行一个端口               firewall-cmd --add-port=8080/tcp --permanent
重载防火墙规则              firewall-cmd --reload
用对外 IP 自测服务          curl http://对外IP:端口/
抓某端口的包看 SYN          tcpdump -nn tcp port 端口

口诀:ping 通只是 L3 通,要 ping->telnet->curl 一层层往上测
      telnet refused 是没监听,timeout 是被丢包(防火墙/绑错地址)

避坑清单

  1. ping 通只代表 IP 层(L3)可达,完全不能说明服务端口连得上
  2. 排查连不上要从下往上分层测:ping 测 L3、telnet 测端口、curl 测应用
  3. ping 走 ICMP 不碰端口,很多机器屏蔽 ICMP,ping 不通也未必网络不通
  4. telnet 报 refused 是端口没人监听,timeout 是包被丢,两者根因不同
  5. 服务绑 127.0.0.1 只有本机能连,要让别人连得绑 0.0.0.0
  6. 服务端自测要用对外 IP,用 localhost 会因走回环而产生服务正常的错觉
  7. ss -lntp 看端口的监听地址和监听进程,是服务端排查第一命令
  8. CentOS 7 防火墙是 firewalld,firewall-cmd --list-all 看放行情况
  9. 放行端口加 --permanent 后必须 firewall-cmd --reload 才生效
  10. 云服务器在系统防火墙之外还有一层安全组,两层都放行才通

总结

这次"ping 明明通了、服务却死活连不上"的事故,纠正了我一个用了很多年、却从没认真审视过的思维习惯——把 ping 当成判断"网络好不好"的总开关。在我过去的认知里,排查一个网络问题,流程几乎是条件反射式的:先 ping 一下。ping 不通,那就是网络的事;ping 通了,我心里就"咔哒"一声,给"网络"这一项画上一个大大的对勾,然后心安理得地掉头,去服务的代码里、配置里找原因。ping 通,在我心里几乎就等于"网络这块,放心,没问题"。正是这个根深蒂固的等号,让我这次在错误的方向上消耗了大量时间——ping 通了,我便深信不疑地认定问题在服务本身,于是对着服务的配置翻来覆去,而真正的两个病根,自始至终都安安静静地待在我已经画了对勾的那个"网络"里。复盘到根上,我才真正想清楚一件事:"A 连上 B 的服务"这句话,听起来像一个动作,可它实际上是一长串前后相扣的条件,缺一不可。最底下,是物理链路和网卡要通;往上一层,是 IP 包能在 A 和 B 之间被正确地路由、往返;再往上,是 B 上那个具体的端口,要能完成 TCP 的握手;最顶上,才轮到那个端口背后,真有一个健康的进程在处理请求。这是层层叠叠的一摞条件。而 ping 这个工具,它走的是 ICMP 协议,这个协议的世界里压根就没有"端口"这个概念——它能验证的,从头到尾就只有那么一层:IP 包能不能在两台机器间往返,也就是这摞条件里靠下的那一层。ping 通,它诚实地、且仅仅地告诉了我这一层是好的。可我却把这一层的"好",慷慨地、错误地,推广成了整摞条件的"好"。它上面那几层——端口有没有进程在监听、防火墙这道暗门放不放行、服务到底绑在了哪个地址上——ping 一层都没碰过,它根本没有能力对那几层发表任何意见。而这次的两个真凶,恰恰齐刷刷地藏在 ping 视野之上的那几层里:一个,是 B 的服务把自己绑死在了 127.0.0.1 上,这个地址是机器的"自言自语"频道,只有 B 自己听得见,外面的 A 永远够不着——更阴险的是,我在 B 上用 localhost 自测时它一切正常,这份"正常"反过来又麻痹了我;另一个,是 B 的 firewalld 这道门卫,压根没把 8080 端口列进放行名单,A 递过来的 TCP 握手包,在门口就被悄无声息地丢掉了。这两个真凶,没有一个属于服务的代码逻辑,它们全都属于我那个被 ping 一通就轻率盖章的"网络"。这次从一个"ping 通却连不上"的死结里走出来,我最大的收获,是把脑子里那个浑然一块的"网络"概念,拆成了清清楚楚的一层一层。ping 通是个好消息,但它是一句范围极其有限的好消息,它只为最底下那层背书。真正可靠的排查,是放弃寻找"网络好不好"这个根本不存在的总开关,转而老老实实地从下往上,一层一层地问过去:IP 层通吗?端口层通吗?应用层通吗?在哪一层第一次得到"不通"的回答,真正的问题,就端端正正地坐在那一层里等着你。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
Linux教程

nginx 配置全对却 403:一次 Linux SELinux 安全上下文排查复盘

2026-5-20 19:57:34

Linux教程

CPU 不高 load 却爆表:一次 Linux 磁盘 IO 瓶颈排查复盘

2026-5-20 20:07:14

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索