LV040-DHCP与IP地址自动获取
一、 IP 地址的两种来源
1. 静态配置与 DHCP
设备刚接入网络时,它自己并不知道该用什么 IP。有两种做法:
| 方式 | 谁决定地址 | 优点 | 缺点 |
|---|---|---|---|
| 静态配置 | 人(写死或用命令行) | 地址可预测,环境不受服务器影响 | 要手工维护,容易冲突或写错 |
| DHCP 自动获取 | 网络里的 DHCP 服务器 | 即插即用,不冲突 | 依赖服务器;地址不固定 |
家里的手机、电脑连 WiFi 都是第二种,所以从来不用配 IP;而本工程用的是第一种,地址写死在宏里。
2. 两种方式的取舍
这两种方式没有优劣,只有"适合什么场景":
- 静态配置适合"地址必须被外部知道"的场景。比如服务器、打印机、以及本工程的 Bootloader——PC 上的脚本得知道去连谁。
- DHCP 适合"设备频繁加入离开、地址不必固定"的场景。比如咖啡厅的 WiFi、办公室的访客网络。
本工程选静态的完整理由见第五节。
二、 DHCP 在协议栈里的位置
1. 跑在 UDP 上的应用层协议
DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)本身是一个应用层协议,运行在 UDP 之上:
| 项 | 值 |
|---|---|
| 传输层协议 | UDP |
| 服务器端口 | 67 |
| 客户端端口 | 68 |
| 广播地址 | 255.255.255.255 |
这里有个绕不开的循环:要靠 DHCP 拿 IP,可发 DHCP 报文本身又需要 IP。
2. "先有鸡还是先有蛋"的绕法
解决办法是用特殊地址把这一步绕过去:
- 源 IP 用
0.0.0.0("我还没有地址"); - 目的 IP 用
255.255.255.255(有限广播,本网段所有设备); - 目的 MAC 用
FF:FF:FF:FF:FF:FF。
这三个值分别对应 LV010 第三节里提到的 0.0.0.0/8,以及 LV030 第三节里的广播地址。也就是说,ARP 与广播机制才是 DHCP 能起步的前提。
从这个角度看协议栈的依赖顺序很清楚:链路层收发包 → ARP / IP → UDP → DHCP,一层都不能跳。
三、 DORA 四步交互
1. 四步各自做什么
这四个步骤合称 DORA:
| 步骤 | 名称 | 方向 | 内容 |
|---|---|---|---|
| 1 | DISCOVER(发现) | 客户端 → 广播 | "本网段有没有 DHCP 服务器?我需要一个地址" |
| 2 | OFFER(提供) | 服务器 → 客户端 | "我这有个 192.168.16.100,要不要" |
| 3 | REQUEST(请求) | 客户端 → 广播 | "我选定 192.168.16.100 了" |
| 4 | ACK(确认) | 服务器 → 客户端 | "准了,租给你 24 小时",同时给出掩码、网关、DNS |
2. 第 3 步为什么还要广播
有一个细节常被问:第 3 步为什么还要广播?
因为网络里可能有多台 DHCP 服务器,第 2 步它们可能都给了 OFFER。客户端选定其中一个后,必须广播一声让落选的那些把候选地址收回去,否则那些地址就被白白占住了。
3. 第 4 步之后的补充动作
第 4 步之后,客户端还会用 ARP 检查一下这个地址有没有被占用(免费 ARP,见 LV030 第五节),确认没冲突才正式启用。这一步在实验室里遇到"地址冲突"告警时值得留个心眼。
四、 租约与续租
1. DHCP 给的不只是一串 IP
一次 ACK 里会带回一整套参数,这正是"插上网线就能上网"的原因:
| 参数 | 作用 |
|---|---|
| IP 地址 | 自己的地址 |
| 子网掩码 | 判断同网段 |
| 默认网关 | 跨网段时的第一站(见 LV015) |
| DNS 服务器 | 域名 → IP 的解析服务器 |
| 租约时长 | 这个地址能用多久 |
一次拿到全部参数,这正是 DHCP 相比"手工填四个框"的最大价值:它不只分配地址,还顺带把"这个网络该怎么走"一起告诉了你。
2. T1、T2 与租期
DHCP 分配的地址是租给你的,不是永久给你:
| 时间点 | 动作 |
|---|---|
| 到租期的 50%(T1) | 客户端单播给原服务器请求续租 |
| 到租期的 87.5%(T2) | 如果还没续上,广播找任意服务器 |
| 租期到 | 必须停止使用该地址 |
这样做的目的是:设备离开网络后,地址能被回收再分配给别人,不至于浪费。
两个时间点的设计意图也不一样:T1 是"优先找原服务器"(保持地址稳定),T2 是"保底求助"(先保证不断网,哪怕换个服务器)。
五、 本工程为什么不用 DHCP
1. 三条理由
这不是"实现不了",而是故意不要,原因有三层:
(1)内存预算紧。本工程 lwipopts.h 里 MEM_SIZE 只有 8 KB、PBUF_POOL_SIZE 只有 8 个,DHCP 需要额外的状态机与报文缓冲,而 LWIP_DHCP 宏被显式关掉了(dhcp.c 虽然编进工程,但被宏裁成了空实现)。
(2)地址必须可预测。Bootloader 的升级流程是"PC 上的脚本主动去连板子",脚本里写死了 192.168.16.30。如果地址是 DHCP 随机分配的,脚本根本不知道该连谁——这是嵌入式升级场景里最实际的理由。
(3)不需要长期在线。DHCP 的价值在"设备频繁加入离开的网络"。而这块板子是"插上线、升级、跳转 APP",全程几分钟,静态地址更简单可靠。
2. 产品里的常见做法
那产品里怎么做?常见组合是**"静态地址 + 存到非易失存储"**:默认给一个出厂地址,需要改的时候由上位机下发新地址,写进外部 SPI Flash(本工程的 paramset / paramget 命令与 W25Q128 驱动就是为这类需求准备的)。
这条路线保留了静态地址"可预测"的优点,又避免了"改地址要重新编译烧录"的缺点。
3. 169.254 地址:DHCP 失败的标志
如果 PC 设成了"自动获取 IP",而网络里根本没有 DHCP 服务器,PC 会退而求其次,自己挑一个 169.254.x.x 用(叫链路本地地址,Windows 上会提示"受限制的连接")。
这个地址段在同一链路上靠 ARP 还能互相通信,但不可路由(出不了本网段)。
排错意义很大:只要看到 PC 拿到 169.254 开头的地址,就说明"地址没配好",此时去 ping 板子必然不通——不用再看网线、交换机,直接回去手工填一个同网段 IP 即可。
六、 小结与参考资料
1. 本篇小结
- IP 可以静态配置,也可以由 DHCP 自动分配;本工程用静态,理由是内存紧张、地址要可预测、不需要长期在线。
- DHCP 是应用层协议,跑在 UDP 上(服务器 67 / 客户端 68),起步时用
0.0.0.0和255.255.255.255绕开"没有 IP"的死循环。 - DORA 四步:DISCOVER → OFFER → REQUEST → ACK;REQUEST 要广播是为了让落选的服务器回收地址。
- DHCP 顺带下发掩码、网关、DNS;地址是"租"的,T1 找原服务器续租、T2 广播兜底。
- PC 拿到
169.254.x.x就是 DHCP 失败的标志,也是排查的起点。 - 本工程
LWIP_DHCP = 0,dhcp.c被宏裁成空实现,仅作源码对照。
2. 参考资料
- 本工程网络配置(静态宏):
lwip_comm.h - 协议栈裁剪(
LWIP_DHCP = 0):lwipopts.h - DHCP 源码(被宏裁空,可作对照阅读):
dhcp.c - 手册:
STM32F1 LWIP开发手册(DM9000版)_V1.1.pdf第 1.4 节
本文档由 markdowncli 技能辅助生成