Skip to content

12|网络基础

ssh 连不上服务器、浏览器打不开网站、服务起了但外部访问不了——这些"网络不通"的问题,排查起来经常一头雾水,因为不知道是哪一层断了。

排查网络问题绕不开几个核心概念:IP 地址、子网掩码、网关、DNS、端口、TCP 连接。后面的网络配置命令(ipssnmcli)和诊断命令(pingtraceroutecurl)都建立在这些概念上。本篇讲清楚这几个概念,再给出网络问题的标准排查顺序。

一、IP 地址

IP 地址是主机在网络中的标识。Linux 上一张网卡可以绑定一个或多个 IP,服务在监听时可以选择绑定特定 IP,也可以绑定所有 IP——这个选择直接决定外部能否访问。

IPv4 地址的基本结构

IPv4 地址由 32 位二进制组成,通常写成"点分十进制"格式:

192.168.1.10

每段范围 0-255,共 4 段。

公网地址与私有地址

按使用范围,IP 地址分为公网地址和私有地址:

类别地址范围用途
私有地址 A 类10.0.0.0/8内网通信,大型企业内网
私有地址 B 类172.16.0.0/12内网通信
私有地址 C 类192.168.0.0/16内网通信,家庭/小型办公
公网地址其他大部分地址互联网公网通信

私有地址不能在公网上路由——只能在局域网或内网中使用。服务器之间的内网通信通常在这三个网段中。

特殊地址

地址含义
127.0.0.1IPv4 回环地址(localhost),只能本机访问自己
0.0.0.0服务监听时表示"本机所有 IPv4 地址"
::1IPv6 回环地址
::IPv6 监听时表示"本机所有 IPv6 地址"
255.255.255.255广播地址

监听地址陷阱:127.0.0.1 vs 0.0.0.0

一个极其常见的故障场景:服务监听了 127.0.0.1:8080,本机 curl 127.0.0.1:8080 完全正常,但外部机器死活连不上,即使把防火墙完全关闭也连不上——根本原因是服务从一开始就没有接受来自其他 IP 的连接。

验证当前服务的监听地址:

bash
$ ss -lntp | grep 8080
LISTEN 0 128 127.0.0.1:8080 0.0.0.0:* users:(("myapp",pid=1234,fd=3))

如果 Local Address 显示 127.0.0.1:8080,即便从 192.168.1.x 等其他网卡访问也无法连通。修复方式是修改服务配置,把监听地址改为 0.0.0.0:8080(监听所有网卡)或具体的网卡 IP(如 192.168.1.10:8080)。

排查"服务起来了但外部访问不了"的故障,第一步就应当检查监听地址,而不是先查防火墙。 三种常见监听地址的访问范围:

监听方式谁能访问
127.0.0.1:8080仅本机
0.0.0.0:8080所有网卡、所有 IP 都能访问
192.168.1.10:8080只能通过 192.168.1.10 这个 IP 访问

二、子网掩码与 CIDR

子网掩码用于区分 IP 地址中哪部分是"网络部分",哪部分是"主机部分"。同一个网段内的机器(网络部分相同)可以直接通信;不同网段的机器必须经过网关转发。

CIDR 表示法

CIDR(Classless Inter-Domain Routing,无类别域间路由)把子网掩码长度直接写在 IP 后面:

192.168.1.10/24

/24 表示前 24 位是网络位,剩余 8 位是主机位。这个表示法等同于子网掩码 255.255.255.0

对于 192.168.1.10/24:

项目
网络地址192.168.1.0(主机位全 0)
广播地址192.168.1.255(主机位全 1)
可用主机范围192.168.1.1192.168.1.254
可用主机数254

注意:网络地址和广播地址不能分配给主机使用。

常见 CIDR 对照

CIDR子网掩码可用主机数
/8255.0.0.016,777,214
/16255.255.0.065,534
/24255.255.255.0254
/25255.255.255.128126
/26255.255.255.19262
/27255.255.255.22430
/28255.255.255.24014
/29255.255.255.2486
/30255.255.255.2522
/32255.255.255.2551(主机路由)

同网段判定陷阱

两台机器是否在同一网段,不是看 IP 地址外观相似,而是看 IP + 掩码计算出的网络地址是否一致:

192.168.1.200/24 → 网络地址 192.168.1.0/24
192.168.1.200/25 → 网络地址 192.168.1.128/25

同样是 192.168.1.200,掩码不同,所属网段完全不同:

  • /24 网段包含 192.168.1.0192.168.1.255
  • /25 网段(下半段)包含 192.168.1.128192.168.1.255

云主机修改网络配置时把掩码填错,会出现"IP 看起来正确,但就是不通"的诡异现象——某个看似在同网段的目标 IP,因为掩码不同,实际在不同网段,需要走网关转发,但路由表又没配置。

三、网关与路由

默认网关

默认网关是内网主机通往外部网络的出口。当访问的目标地址不在本机直连的网段内时,数据包就发送给默认网关,由网关负责往下一跳转发

查看路由表

bash
ip route

输出示例:

default via 192.168.1.1 dev eth0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10
10.0.0.0/16 via 192.168.1.254 dev eth0

逐行解读:

第 1 行:default via 192.168.1.1 dev eth0

  • default 表示默认路由,匹配所有目标地址不在其他路由条目中的流量
  • via 192.168.1.1 表示下一跳是 192.168.1.1(即默认网关)
  • dev eth0 表示通过 eth0 网卡发出

第 2 行:192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10

  • 这是直连网段路由,本机直接连接到了 192.168.1.0/24 网段
  • 该网段的流量直接从 eth0 发出,源地址使用 192.168.1.10
  • 不需要经过网关转发

第 3 行:10.0.0.0/16 via 192.168.1.254 dev eth0

  • 静态路由,目标 10.0.0.0/16 网段经过 192.168.1.254 转发
  • 适用于需要访问特定内网网段但不通过默认网关的场景

最长前缀匹配原则

路由匹配遵循"最长前缀优先"原则——当一个目标地址匹配多条路由时,选择子网掩码最长(最精确)的那一条。

例如目标 10.10.1.2,路由表中有:

10.0.0.0/8 via 192.168.1.1
10.10.0.0/16 via 192.168.1.254

10.10.1.2 同时匹配这两条,内核会选择 /16(更精确),数据包发往 192.168.1.254

理解这一原则后,排查"流量为什么走了错误的下一跳"这类问题就有了清晰的方向——查看路由表中所有匹配条目,找出最长前缀的那一条。

路由相关命令

bash
ip route                          # 查看路由表
ip route get 10.10.1.2            # 查看到达指定 IP 会走哪条路由
ip route add 10.0.0.0/16 via 192.168.1.254    # 添加静态路由
ip route del 10.0.0.0/16          # 删除路由

ip route get 非常实用——直接告诉你"发往某个 IP 的包会从哪个网卡、用什么源地址、经过哪个下一跳出去":

bash
$ ip route get 8.8.8.8
8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.10 uid 1000

四、DNS

DNS(Domain Name System)把人类容易记忆的域名翻译成机器使用的 IP 地址。

查看当前 DNS 服务器

bash
cat /etc/resolv.conf

输出示例:

nameserver 192.168.1.1
nameserver 8.8.8.8
search example.com

nameserver 指定使用的 DNS 服务器,search 指定后缀,可以让用户输入短名时自动补全。

解析顺序由 nsswitch.conf 控制

Linux 的域名解析顺序由 /etc/nsswitch.conf 中的 hosts 行决定,不是直接走 DNS:

bash
$ grep '^hosts:' /etc/nsswitch.conf
hosts: files dns

files dns 表示先查 /etc/hosts 文件,再查 DNS。这是绝大多数发行版的默认顺序。

/etc/hosts 优先级陷阱

线上常见的故障场景:DNS 已经修改,但机器始终解析到旧 IP,排查后发现是 /etc/hosts 中写死了一个旧地址

因为 /etc/hosts 在解析顺序中优先于 DNS,只要 hosts 文件中存在对应记录,DNS 的修改完全无效。

排查域名解析异常时,/etc/hostsnsswitch.conf 必须一起查:

bash
# 第 1 步:查看 hosts 文件中是否有该域名的硬编码
grep -i "example.com" /etc/hosts

# 第 2 步:查看解析顺序
grep '^hosts:' /etc/nsswitch.conf

# 第 3 步:对比实际解析结果与 DNS 查询结果
getent hosts example.com
dig example.com +short

几个解析命令的差异

不同的解析命令走的路径不同,适合不同的排查目的:

命令路径适用场景
getent hosts example.com走系统 NSS(包括 hosts、DNS、LDAP)模拟应用真实的解析路径
dig example.com直接查 DNS,绕过 hosts 和本地缓存确认 DNS 服务器返回的内容
dig @8.8.8.8 example.com强制使用指定的 DNS 服务器对比不同 DNS 服务器的返回
nslookup example.com类似 dig,但输出更简略快速验证
host example.com类似 dig 的简化版本快速验证

排查"应用解析到的 IP 与预期不符"时使用 getent hosts——它的解析路径与应用程序完全一致(包括 hosts、DNS 等),返回的结果就是应用看到的结果。dig 直接查 DNS,绕过 hosts 文件,适合用来确认 DNS 服务器本身的返回值是什么。

dig 输出解读

bash
$ dig example.com

; <<>> DiG 9.16.23-RH <<>> example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12345
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; ANSWER SECTION:
example.com.        86400   IN  A   93.184.216.34

;; Query time: 25 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
;; WHEN: Tue Jun 23 10:30:00 CST 2026

关键字段:

  • status: NOERROR — 解析成功
  • ANSWER SECTION — 返回的 IP
  • 86400 — TTL(缓存有效期,86400 秒 = 24 小时)
  • SERVER: 192.168.1.1#53 — 实际使用的 DNS 服务器

其他可能的 status:

  • NXDOMAIN — 域名不存在
  • SERVFAIL — DNS 服务器内部错误
  • REFUSED — DNS 服务器拒绝回答

五、端口

端口是传输层(TCP/UDP)用于区分同一台机器上不同服务的编号。同一个 IP 上可以同时运行 HTTP(80)、SSH(22)、MySQL(3306),靠端口号区分。

端口号范围

范围名称用途
0-1023知名端口(Well-known)系统服务,绑定需要 root 权限
1024-49151注册端口应用程序常用
49152-65535临时/私有端口客户端发起连接时随机选取

常见服务的默认端口

端口协议服务
22TCPSSH
80TCPHTTP
443TCPHTTPS
3306TCPMySQL
5432TCPPostgreSQL
6379TCPRedis
27017TCPMongoDB
53TCP / UDPDNS
25TCPSMTP
123UDPNTP
161UDPSNMP

端口通不通的分层判断

"端口通不通"实际上要分多个层面判断,不是一条命令能搞定的:

  1. 本机服务是否在监听 — ss -lntp
  2. 本机防火墙是否放行 — iptables -L / firewall-cmd --list-all
  3. 云平台安全组是否放行(云环境)
  4. 中间网络是否可达 — tracerouteping
  5. 对端防火墙是否拦截 — 联系对端运维

排查时分层确认,从近到远逐步排查:

bash
# 本机视角
ss -lntp | grep ':80'                       # 服务监听了没

# 同网段视角
nc -zv 192.168.1.10 80                     # 端口能否连接

# 跨网段视角
traceroute 192.168.1.10                    # 路径上哪一跳出问题

ss 命令查看端口

bash
ss -lntp           # 查看所有 TCP 监听端口
ss -lnup           # 查看所有 UDP 监听端口
ss -lntp | grep ':80'    # 查看 80 端口的监听情况
ss -ant            # 查看所有 TCP 连接(包括 ESTABLISHED)
ss -ant state established  # 仅看已建立的连接

参数含义:-l 监听、-n 数字端口(不解析为服务名)、-t TCP、-u UDP、-p 显示进程信息、-a 所有状态。

六、网络问题的标准排查顺序

OSI 七层模型在教科书中作为标准答案出现,但实际排障不需要严格背诵。更有用的是按层次从下往上逐步排查的思维方式:

步骤确认什么命令
1网卡是否存在、是否有 IPip addr
2路由是否正确ip routeip route get <目标>
3DNS 能否正常解析getent hosts <域名>
4本机端口是否监听ss -lntp
5TCP 连接能否建立curl -vnc -zv
6应用是否正常响应查应用日志、查返回内容

这个从下往上的顺序通常比一上来就抓包更快定位问题。底层断了上层一定不通,先确认底层无问题再向上排查,效率最高。

例如"应用 A 调用应用 B 失败"的标准排查:

bash
# 1. 应用 A 所在主机的网络配置是否正常
ip addr
ip route get <应用B的IP>

# 2. 能否解析应用 B 的域名
getent hosts api.example.com

# 3. 能否到达应用 B 的端口
nc -zv api.example.com 8080

# 4. HTTP 层能否得到响应
curl -v http://api.example.com:8080/healthz

# 5. 应用 B 的日志中是否收到了请求
journalctl -u app-b -n 50

七、TCP 三次握手

TCP 传输数据前需要先建立连接,这一过程称为三次握手。本质是双方互相确认"我能发、你能收"的过程,分三步:

步骤方向标志含义
1客户端 → 服务端SYN"我想与你建立连接"
2服务端 → 客户端SYN-ACK"收到,我也准备好了"
3客户端 → 服务端ACK"收到你的确认,开始传数据"

三次握手过程的可视化

两种典型连接失败的本质区别

理解三次握手后,可以解释两种常见的连接失败现象。

情况 1:服务端端口未监听

客户端发送 SYN 后,服务端会回应 RST(Reset)包。客户端立即收到 Connection refused 错误。

  • 特征:错误立即出现(毫秒级)
  • 结论:数据包到达了目标机器,但目标端口上没有服务在监听
  • 下一步排查:目标机器上 ss -lntp 确认服务监听状态

情况 2:中间网络或防火墙丢弃 SYN 包

客户端持续重发 SYN,但收不到任何回应(SYN、SYN-ACK、RST 都没有)。最终客户端 TCP 协议栈超时,返回 Connection timed out

  • 特征:错误经过一段时间后才出现(默认 TCP 协议栈重试需要数十秒)
  • 结论:数据包根本没到达目标机器,或目标机器没回应
  • 下一步排查:traceroute 看路径在哪一跳断了,检查防火墙、路由

Connection refused vs Connection timed out

这两个错误反映了完全不同的故障,排查方向也完全不同:

错误含义时间特征排查方向
Connection refused到达对方但被拒立即返回服务端口未监听
Connection timed out根本到不了对方延迟返回网络路径、防火墙、对方主机离线

混淆这两个错误会让排查方向完全错位——把端口未监听当成网络故障去查路由,或把网络不通当成服务故障去查应用日志。

UDP 没有三次握手

UDP 是无连接协议,没有三次握手机制。判断"UDP 端口通不通"没有 TCP 那么直观——客户端把数据包发出去后,无法确认对方收到了没。

UDP 服务(DNS:53/UDP、NTP:123/UDP、SNMP:161/UDP)排查时:

  • 看应用层是否返回响应(dig 看 DNS 响应、ntpdate -q 看 NTP 响应)
  • 或在双方机器上抓包,确认 UDP 包是否真正到达
  • 不能简单用 nc -zv 判断 UDP 端口连通性(它只能确认本地能否发送,不能确认对方是否收到)

UDP 端口连通性的可靠测试方式:

bash
# 客户端发送一个测试包
echo "test" | nc -u -w 1 192.168.1.10 53

# 在服务端抓包确认是否收到
tcpdump -i any -nn udp port 53