Appearance
12|网络基础
ssh 连不上服务器、浏览器打不开网站、服务起了但外部访问不了——这些"网络不通"的问题,排查起来经常一头雾水,因为不知道是哪一层断了。
排查网络问题绕不开几个核心概念:IP 地址、子网掩码、网关、DNS、端口、TCP 连接。后面的网络配置命令(ip、ss、nmcli)和诊断命令(ping、traceroute、curl)都建立在这些概念上。本篇讲清楚这几个概念,再给出网络问题的标准排查顺序。
一、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.1 | IPv4 回环地址(localhost),只能本机访问自己 |
0.0.0.0 | 服务监听时表示"本机所有 IPv4 地址" |
::1 | IPv6 回环地址 |
:: | 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.1 到 192.168.1.254 |
| 可用主机数 | 254 |
注意:网络地址和广播地址不能分配给主机使用。
常见 CIDR 对照
| CIDR | 子网掩码 | 可用主机数 |
|---|---|---|
/8 | 255.0.0.0 | 16,777,214 |
/16 | 255.255.0.0 | 65,534 |
/24 | 255.255.255.0 | 254 |
/25 | 255.255.255.128 | 126 |
/26 | 255.255.255.192 | 62 |
/27 | 255.255.255.224 | 30 |
/28 | 255.255.255.240 | 14 |
/29 | 255.255.255.248 | 6 |
/30 | 255.255.255.252 | 2 |
/32 | 255.255.255.255 | 1(主机路由) |
同网段判定陷阱
两台机器是否在同一网段,不是看 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.0到192.168.1.255/25网段(下半段)包含192.168.1.128到192.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.25410.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.comnameserver 指定使用的 DNS 服务器,search 指定后缀,可以让用户输入短名时自动补全。
解析顺序由 nsswitch.conf 控制
Linux 的域名解析顺序由 /etc/nsswitch.conf 中的 hosts 行决定,不是直接走 DNS:
bash
$ grep '^hosts:' /etc/nsswitch.conf
hosts: files dnsfiles dns 表示先查 /etc/hosts 文件,再查 DNS。这是绝大多数发行版的默认顺序。
/etc/hosts 优先级陷阱
线上常见的故障场景:DNS 已经修改,但机器始终解析到旧 IP,排查后发现是 /etc/hosts 中写死了一个旧地址。
因为 /etc/hosts 在解析顺序中优先于 DNS,只要 hosts 文件中存在对应记录,DNS 的修改完全无效。
排查域名解析异常时,/etc/hosts 与 nsswitch.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— 返回的 IP86400— 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 | 临时/私有端口 | 客户端发起连接时随机选取 |
常见服务的默认端口
| 端口 | 协议 | 服务 |
|---|---|---|
| 22 | TCP | SSH |
| 80 | TCP | HTTP |
| 443 | TCP | HTTPS |
| 3306 | TCP | MySQL |
| 5432 | TCP | PostgreSQL |
| 6379 | TCP | Redis |
| 27017 | TCP | MongoDB |
| 53 | TCP / UDP | DNS |
| 25 | TCP | SMTP |
| 123 | UDP | NTP |
| 161 | UDP | SNMP |
端口通不通的分层判断
"端口通不通"实际上要分多个层面判断,不是一条命令能搞定的:
- 本机服务是否在监听 —
ss -lntp - 本机防火墙是否放行 —
iptables -L/firewall-cmd --list-all - 云平台安全组是否放行(云环境)
- 中间网络是否可达 —
traceroute、ping - 对端防火墙是否拦截 — 联系对端运维
排查时分层确认,从近到远逐步排查:
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 | 网卡是否存在、是否有 IP | ip addr |
| 2 | 路由是否正确 | ip route、ip route get <目标> |
| 3 | DNS 能否正常解析 | getent hosts <域名> |
| 4 | 本机端口是否监听 | ss -lntp |
| 5 | TCP 连接能否建立 | curl -v、nc -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