图 1:本文封面,新设备入网时三个角色的协作顺序。
同一批的真题解析把 ARP 这一个协议讲透了:报文长什么样、缓存怎么建、免费 ARP 干什么用。这篇换一个更大的视角——一台新设备插上网线或连上 Wi-Fi,从「一无所有」到发出第一个数据包,到底有哪些协议在暗中接力。把这条时间线走一遍,局域网通信的全景就有了。
第一幕:DHCP,先给自己拿个身份。
新设备此刻没有 IP 地址,在网络上等于没有身份。它的第一步是广播一个 DHCP Discover:源地址填全零、目的地址填全一的广播地址,意思是「我不知道我是谁,也不知道服务器在哪,但谁管地址请回话」。这是 RFC 2131 定义的四步握手的第一拍。
网段里的 DHCP 服务器收到后回一个 Offer:我这有个空闲地址,连同子网掩码、默认网关、DNS 服务器一起报给你。设备可能收到多台服务器的 Offer,挑一个之后,再广播一次 Request 宣布「我选了这台」——这一步仍然是广播,因为落选的服务器也要听到,好把预留的地址收回去。最后被选中的服务器回 ACK,地址正式生效。
还有个关键细节:分到的不是永久产权,而是租约。租约到期前设备要主动续约,否则地址被回收、分给别的新设备。所以重启路由器之后家里设备的 IP 变了,这不是故障,是租约机制在正常工作。
第二幕:ARP,找到邻居的门牌号。
拿到 IP 只解决了三层的事。数据包出了网卡是二层的帧,帧上写的是 MAC 地址。设备想访问外网,第一跳是默认网关,但它只知道网关的 IP,不知道网关的 MAC。于是 ARP 出场:向全网广播「谁的 IP 是网关地址,请告诉我你的 MAC」,网关单播应答,结果写进本机 ARP 缓存并带上生存时间,下次直接用、不用再问。报文格式与缓存机制的细节同批真题已经展开,这里只记住它在接力中的位置:三层地址到二层地址的翻译官。
第三幕:交换机,边转发边学习的二传手。
帧离开网卡后先到交换机。交换机不靠人工配置转发表,它靠自学习:每收到一个帧,先把「源 MAC 对应入端口」记进 MAC 表;再查目的 MAC,查到了就定向转发到对应端口,查不到就向其余所有端口泛洪(广播帧天然泛洪)。学习在前、转发在后,网络跑一会儿之后表就收敛了,泛洪越来越少。用一段 Node.js 脚本把这套机制模拟出来:
// 模拟交换机的 MAC 地址学习:收到帧先记「源 MAC → 入端口」,再按目的 MAC 查表转发,查不到就泛洪
const macTable = new Map();
function frame(srcMac, dstMac, inPort, payload) {
macTable.set(srcMac, inPort); // 第一步永远是学习:源地址一定在入端口方向
const known = macTable.get(dstMac);
if (known !== undefined) {
console.log(`帧 ${srcMac} → ${dstMac} 从端口 ${inPort} 进入:学习 ${srcMac}→端口${inPort},查表命中,定向转发到端口 ${known}(${payload})`);
} else {
console.log(`帧 ${srcMac} → ${dstMac} 从端口 ${inPort} 进入:学习 ${srcMac}→端口${inPort},目的未知,向其余所有端口泛洪(${payload})`);
}
}
frame('AA:AA', 'BB:BB', 1, 'A 发给 B 的 ARP 请求广播');
frame('BB:BB', 'AA:AA', 2, 'B 回给 A 的 ARP 应答单播');
frame('CC:CC', 'AA:AA', 3, 'C 发给 A 的数据帧');
frame('AA:AA', 'CC:CC', 1, 'A 回给 C 的数据帧');
console.log('---\n最终 MAC 表:', Object.fromEntries(macTable));
实测输出:
帧 AA:AA → BB:BB 从端口 1 进入:学习 AA:AA→端口1,目的未知,向其余所有端口泛洪(A 发给 B 的 ARP 请求广播)
帧 BB:BB → AA:AA 从端口 2 进入:学习 BB:BB→端口2,查表命中,定向转发到端口 1(B 回给 A 的 ARP 应答单播)
帧 CC:CC → AA:AA 从端口 3 进入:学习 CC:CC→端口3,查表命中,定向转发到端口 1(C 发给 A 的数据帧)
帧 AA:AA → CC:CC 从端口 1 进入:学习 AA:AA→端口1,查表命中,定向转发到端口 3(A 回给 C 的数据帧)
---
最终 MAC 表: { 'AA:AA': 1, 'BB:BB': 2, 'CC:CC': 3 }
注意第一帧泛洪、后面全部定向转发:这正是「先学习后收敛」的过程,ARP 应答只跑一个端口,不用再惊动全网。
图 2:四幕接力的全景时序。
第四幕:ICMP,出错时有人报信。
正常通信之外还需要一条「报丧与问路」的通道,这就是 ICMP。它没有独立的协议栈,直接装在 IP 包里(协议号 1),是每个 IP 实现都必须携带的组成部分。你最熟悉的 ping,就是 ICMP 回显请求(类型 8)与回显应答(类型 0)的一来一回。另一类是差错报告:数据包的 TTL 被沿途一路减到零,转发它的路由器会丢弃它并回送一个超时报文(类型 11)告诉发送方,traceroute 正是靠故意调小 TTL 把沿途路由器一个个钓出来;目的网络、主机或端口不可达时,则回送目的不可达报文(类型 3)。RFC 792 说得很明白:ICMP 的目的是提供通信环境的反馈,不是让 IP 变得可靠——包丢了,没人保证一定有人报。
把四幕串成一条线。
回看全景:DHCP 发身份(IP、网关、DNS),ARP 找邻居(把 IP 翻译成 MAC),交换机记门牌并定向转发,ICMP 在出错时回送信使。四者各管一段、缺一不可:没有 DHCP 你得手工配地址,没有 ARP 三层到不了二层,没有 MAC 学习交换机只能永远泛洪,没有 ICMP 故障全成静默丢包。排障时的动线恰好可以反过来用:先 ping 网关验证链路,不通再查 ARP 表里有没有网关的 MAC,再回头看 IP 是不是 DHCP 正常分到的。
你在局域网排障时遇到过最「玄学」的故障是什么,最后落在哪一层?欢迎分享你的排查故事。