LV050-一次ping的完整旅程
一、 为什么要按时间顺序串一遍
前面几篇是分段讲的:分层(LV001)、封装与帧(LV005)、地址与掩码(LV010)、网关与跨网段(LV015)、设备与转发(LV020)、ARP(LV030)、传输层(LV035)、DHCP(LV040)、调试工具(LV045)。
但真实的通信是一次把好几层都用上,只学分段容易出现"每篇都懂、合起来说不清"的情况。
这一篇按时间顺序把一次 ping 的报文走一遍。设定很具体:
| 角色 | 地址 | 配置 |
|---|---|---|
| 板子 | 192.168.16.30/24 | 静态,网关 192.168.16.1 |
| PC | 192.168.16.100/24 | 手工填,同网段 |
| 连接方式 | 网线直连(或经同一台交换机) | — |
二、 场景一:直连同网段的完整流水账
1. 逐步流水账
在 PC 上敲下 ping 192.168.16.30,接下来发生的事按顺序是:
| # | 谁做 | 做什么 | 依据什么判断 | 对应代码/文件 |
|---|---|---|---|---|
| 1 | PC | 判断目的 IP 与自己是否同网段 | 192.168.16.30 & 255.255.255.0 == 本机网络地址 → 同网段,不走网关 | — |
| 2 | PC | 查 ARP 缓存里有没有 192.168.16.30 的 MAC | 没有 → 发起 ARP | — |
| 3 | PC | 广播 ARP 请求:"谁是 192.168.16.30" | 目的 MAC = FF:FF:FF:FF:FF:FF | — |
| 4 | 板子 DM9000 | 收到帧,触发中断(PG6 下降沿) | 网卡按目的 MAC 过滤后收下 | dm9000.c |
| 5 | 板子主循环 | 取包 → 送进协议栈 | 每 1 ms 由 lwip_comm_poll() 调用 | ethernetif.c、lwip_comm.c |
| 6 | 板子 lwIP | 看以太网帧类型 0x0806 → 交给 ARP | 类型字段分流 | ethernet.c、etharp.c |
| 7 | 板子 lwIP | 记录 PC 的映射,单播回 ARP 应答 | 请求里带了 PC 的 MAC,不必广播 | etharp.c |
| 8 | PC | 收到应答,写入 ARP 缓存 | 之后一段时间不用再问 | — |
| 9 | PC | 构造 ICMP Echo 请求 → 加 IP 头 → 加以太网头(目的 MAC = 板子) | 目的 IP = 192.168.16.30、协议号 1 | — |
| 10 | 板子 | 收到帧,类型 0x0800 → 交给 IP | — | ip4.c |
| 11 | 板子 | IP 头目的地址是自己 → 协议号 1 → 交给 ICMP | — | icmp.c |
| 12 | 板子 | 生成 Echo 应答,交换源/目的地址回发 | — | icmp.c |
| 13 | PC | 收到应答,ping 打印一行 时间=... TTL=... | — | — |
2. 整个过程里最关键的两步
(1)第 1 步决定了"要不要找网关"——同网段就不找。这也是为什么直连实验里网关填错也不影响。
(2)第 7 步的单播决定了后续通信的量级——ARP 只广播一次,之后全靠缓存。如果没有缓存,每个 ICMP 包都会带来一次广播风暴。
3. 第一包为什么慢
再补一个观察:ping 的第一包通常比后续几包慢(经常是几毫秒 vs 零点几毫秒),因为第一包里含了 ARP 的一问一答。
这个现象可以直接反过来用作排错依据:如果连第一包都不通,问题多半在 ARP 或更下面,而不是 IP 以上。
三、 场景二:中间多了一台交换机
1. 流水账一行都不用改
把网线从"直连"改成"各自接到同一台交换机",上面的流水账一行都不用改:
| 差异 | 说明 |
|---|---|
| 板子的 ARP 请求先进交换机 | 交换机看目的 MAC 是广播 → 向其它口泛洪 |
| 交换机学到了 PC 与板子的 MAC | 后续帧就能精准转发,不再泛洪 |
| 板子与 PC 的配置 | 完全不变(仍必须同网段) |
2. 新增的失败点
从"能否 ping 通"的角度看,直连与同交换机是等价的。新增的失败点只有交换机本身:
- 是否通电、指示灯是否正常;
- 端口是否协商成功(百兆/千兆、双工是否匹配);
- 有没有把两个口接成环(会引发广播风暴,现象是网络时通时断)。
四、 场景三:跨网段通信
1. 流水账在前两步就分岔
现在把板子放在 A 网段、PC 放在 B 网段(中间是路由器):
| 角色 | 地址 |
|---|---|
| 板子 | 192.168.16.30/24,网关 192.168.16.1 |
| PC | 192.168.1.100/24,网关 192.168.1.1 |
| 路由器 | 接口 A = 192.168.16.1,接口 B = 192.168.1.1 |
这次的流水账:
| # | 谁做 | 关键动作 |
|---|---|---|
| 1 | PC | 判断不同网段 → 交给默认网关,而不是直接 ARP 板子 |
| 2 | PC | ARP 的是网关的 MAC,不是板子的 |
| 3 | PC | 以太网帧:目的 MAC = 网关,但 IP 头里目的地址仍是 192.168.16.30 |
| 4 | 路由器 | 收到帧 → 查路由表 → 从接口 A 转发出去,并重写链路层帧头(源 MAC 换成自己,TTL 减 1) |
| 5 | 板子 | 收到请求,生成应答;目的 IP 是 192.168.1.100 → 不同网段 → 交给自己的网关 |
| 6 | 路由器 | 反向转发回 PC |
2. 必须记住的结论
IP 头全程不变,以太网头逐跳重写。
这就是"MAC 只在一条链路内有效"的具体含义。也正因为如此,跨网段通信要求双方都能找到网关:
如果板子没配网关(或配了不存在的),它能收到请求(因为请求是路由器送上门的),却发不出去应答——现象就是"PC 一直超时,但板子日志显示确实收到了包"。这个现象很有辨识度,遇到它直接去查网关,不要怀疑收包路径。
本实验只有直连或同交换机两种接法,所以正确做法就是双方配成同网段,而不是去折腾网关。网关本身的机制见 LV015-网关与跨网段通信。
五、 场景四:从局域网到公网
如果要访问 www.example.com 这种外网地址,还要多两件事:
| 环节 | 做什么 |
|---|---|
| DNS | 把域名解析成 IP(否则只能直接敲 IP) |
| NAT | 家用路由器把私有地址 192.168.16.30 换成自己的公网地址,并记住"这个连接是谁的",回包再换回来 |
这也解释了为什么私有地址能重复使用:它只在内网可见,出门就被 NAT 换掉了。
反过来说清本工程的定位:Bootloader 只实现了 IP / ICMP / TCP / UDP / ARP 这一小块,既没有 DHCP 也没有 DNS,也没有公网地址。它的目标始终是"在同一局域网内被 PC 找到并升级",不是上网。
六、 把流水账落回实测输出与排错链
1. 串口实测输出
板子启动后串口会打印(详见 LV020-网络通信实验 第一节):
DM9000: ID = 0x90000A46 ← 网卡认到了,链路层 OK
lwIP: MAC 02-00-00-XX-XX-XX, IP 192.168.16.30 ← 网络层参数就绪
lwIP: RX mode = interrupt (EXTI6 falling edge) ← 收包路径就绪
lwIP: ready, ping 192.168.16.30 to test ← 可以开始场景一了会读这四行,就等于有了四个观测点:任何一行不对,排查范围立刻缩小一个层级。
2. 一张排错决策树
把前面几篇合起来,得到一条从下往上的排查链(顺序不能反):
网口灯亮吗? ── 不亮 ──▶ 网线 / 对端 / PHY 上电
│亮
▼
串口有 DM9000: ID = 0x90000A46 吗? ── 没有 ──▶ FSMC 初始化 / 片选 / 总线时序
│有
▼
PC 拿到的是 169.254.x.x 吗? ── 是 ──▶ 地址没配好,手工填同网段 IP
│不是
▼
两边同网段吗?(IP & 掩码 比对) ── 不同 ──▶ 改成同网段
│同
▼
PC 上 arp -a 能看到板子的 IP 吗? ── 看不到 ──▶ ARP 没通:收包模式 / 中断标志 / 交换机
│看到
▼
还是 ping 不通 ──▶ 才轮到查协议栈上层(ICMP、收发缓冲、计数统计)这条链的价值在于:绝大多数"ping 不通"都发生在最上面三层,而不是 lwIP。
3. 两个与 lwIP 相关的现象
| 现象 | 原理 |
|---|---|
| 第一次 ping 慢、后续快 | 第一包含 ARP 一问一答 |
| ping 通但 TCP 连不上 | 说明 IP / ICMP / ARP 都好,问题在传输层——多半是端口被占或单连接策略(见 网络通信实验 第三节) |
七、 小结与参考资料
1. 本篇小结
- 一次 ping 会把分层、封装、IP 与掩码、设备转发、ARP 全部用上,是检验网络配置最省事的工具。
- 同网段:不走网关,直接 ARP 目的 IP;跨网段:交给网关,ARP 的是网关。
- IP 头全程不变,以太网头逐跳重写。
- 直连与同交换机在"能否 ping 通"上等价,配置要求也相同:双方同网段。
- 上网还要 DNS 与 NAT;本工程的目标不是上网,而是在局域网内被找到。
- 排查顺序永远从物理层往上:先灯、再网卡 ID、再地址、再 ARP,最后才是协议栈。
2. 参考资料
- 实验步骤与实测输出:LV020-网络通信实验
- 网卡驱动:
dm9000.c - 协议栈初始化与轮询:
lwip_comm.c - 手册:
STM32F1 LWIP开发手册(DM9000版)_V1.1.pdf第 1.6 节
本文档由 markdowncli 技能辅助生成