学网络的人很容易产生一个幻觉:只要我知道对方的 IP,数据就能自己飞过去。这句话对了一半。这句话只回答了"终点在哪",却没有回答"路上每一步怎么走"。
从业到现在,我见过太多新手卡在一个问题上:明明两台电脑的 IP 都能 ping 通对方,可是一抓包却看不懂"为什么一个 IP 包外面还要套一层 MAC 地址的壳子"。其实答案就藏在今天这堂课里——数据链路层。
我们这套课已经走过应用层、传输层、网络层,今天终于落到最接近物理介质的一层。它就是那个"打通最后一公里、甚至每一公里"的角色。IP 负责让你"能到达某个远方",而数据链路层负责让你"能跨过眼前这一小段路"。所以先把这句话刻进脑子:IP 是目的地,数据链路是脚下那段路。 接下来的内容,我们就把"这段路"从头到尾刨一遍。
数据链路层是干什么的
数据链路层(Data Link Layer,数据链路层),它的职责一句话就能说清:用于两个设备(同一种数据链路节点)之间进行传递。这里的关键词是"两个设备之间"。它不关心你全程要去哪里,只关心"我是从眼前这个接口,把这一帧数据稳妥地送到下一个接口"。
要理解它的定位,先拆一拆"链路"两个字。所谓链路(link),指的就是两个相邻节点之间的一段物理连接。所谓"数据链路层",就是负责"在这段物理连接上,把数据组织成帧、送出去、并且能发现传错没"的那一层。
我们再把它的活分类,数据链路层大体有这几件核心事务:
- 封装成帧(Framing):把网络层交给它的 IP 数据报,加上头部和尾部,包装成一个完整的数据帧,帧是这一层的运输单位;
- 物理寻址(Physical Addressing):通过 MAC 地址在帧头部标明"这一帧从哪来、往哪去",这样每个接口才能判断"这是不是给我的";
- 差错检测(Error Control):在帧尾部放一个校验码,接收方据此判断这一帧在路上有没有被改坏;
- 介质访问控制(Medium Access Control,介质访问控制):在有多个节点共享同一根介质时,决定"谁先发言",稍后讲碰撞域时会看到以太网经典的 CSMA/CD 就是干这个的;
- 流量控制:在可靠的链路协议(比如 PPP 的某些模式)下,还负责控制发送方不要太快。
一句话概括:网络层负责"全局导航",数据链路层负责"每一小段的可靠传送"。这两者之间到底怎么分工,用我们之前的"白龙马"比喻可以讲得特别清楚。
"白龙马"模型:数据链路层 vs 网络层
如果你跟过这个系列,应该还记得那个"唐僧取经"的例子。这里可以再把它搬出来,因为它是理解"网络层和数据链路层到底谁管什么"的最佳脚手架。
我们把一次网络传输想象成唐僧取经的路途。整个取经之路分成好多个区段,每一段都要有坐骑把唐僧驮过去。请问两个问题:整个取经路的"起点"和"终点"是什么?每一段路上的"起点"和"终点"又是什么?
答案很妙:
- IP 地址描述的是路途总体的起点和终点。就像唐僧取经,从长安出发到灵山,这个"从哪出发、到哪结束"是由 IP 决定的,它回答的是"全局上我要去哪"。
- MAC 地址描述的是路途上的每一个区间的起点和终点。取经路上每隔一段就要换一批白龙马,每匹白龙马只负责自己这一段,它只认识"这一段的门口和下一段的门口",也就是本段链路的两个端口。
所以当你把一串数据从你的电脑发给千里之外的服务器时,真正发生的一帧一帧的传送是"一段一段接力"完成的:IP 是全程的地图,而每一帧上的 MAC 地址只是当前这一段的"门牌"。每一段路的接送,都由数据链路层负责。
从这个模型里你能立刻得到三个非常实用的结论:
- MAC 地址是"会变"的:同样是去往同一个 IP 的包,路过第一跳和第二跳时,帧头里的源/目的 MAC 会不断被改写,但 IP 始终不变。它像赛跑接力里的接力棒,每一棒都在换人,但名次拷向不变。
- IP 是不参与"逐跳"的:IP 只关心最终端点,它不会因为路上换了哪段链路就改变自己。
- 数据链路层与网络层的边界:网络层之上,世界是"端到端"的,觉得中间什么都没发生;数据链路层之下,世界是"逐跳"的,处处都是接力。
想清楚这一层,再看后面的 MAC 地址、以太网帧、ARP、交换机,就全都能串起来了。我们先从"这一段路到底是什么路"讲起,那就是——以太网。
认识以太网
提到数据链路层,绕不开的当然是以太网(Ethernet)。但你得先纠正一个常见误解:"以太网"不是一种具体的网络,而是一种技术标准。它同时包含了数据链路层的内容,也包含了一些物理层的内容。
所谓"既是技术标准、又跨物理与链路两层",意思是:以太网标准里既规定了"设备之间怎么访问介质、怎么组织帧"(链路层),也规定了"网线得用什么材质、插头长什么样、信号怎么发"(物理层)。它睁开眼管的是两层的事,这也是为什么有人把它叫"以太网技术"而不是单纯"以太网的协议"。
以太网这个标准具体管了哪些东西呢:
- 网络拓扑结构:传统以太网用总线(bus)拓扑,现代以太网用星型(star)拓扑,所有设备接到一个中心设备上;
- 介质访问控制方式:共享介质年代用 CSMA/CD(载波监听多路访问/冲突检测),后面讲碰撞域时会详细展开;
- 传输速率:10M、100M、1000M(千兆)、10G(万兆)…… 标准的传输速率是阶梯上升的;
- 物理介质:比如规定网线通常要使用双绞线(Twisted Pair),常见的就是 RJ45 接口的网线,以及更快的场景下用光纤。
以太网的历史很值得一提。1973 年左右,施乐(Xerox)帕洛阿尔托研究中心(PARC)的 Bob Metcalfe 发明了以太网的最初原型,后来施乐、Intel、DEC 三家联合把它推向市场。20 世纪 80 年代,IEEE(电气与电子工程师协会)把它标准化成了 IEEE 802.3 系列,此后以太网一路演化,成为当今应用最广泛的局域网(LAN,Local Area Network,局域网)技术。
说它"应用最广"绝不夸张——你的电脑网口、家里路由器后面连的网线、数据中心机房里那一排排服务器,几乎全是以太网。和以太网并列、但今天已很少见的还有令牌环网(Token Ring)、无线 LAN(Wireless LAN,无线局域网,也就是 Wi-Fi 的底层)等。你看同类竞争者这么多,最后是有限的——它们大多被扫进历史,只有以太网几乎一统局域网江湖。
以太网帧格式
有了技术标准的框架,就该看它的"肉身"了——以太网帧(Ethernet Frame,以太网帧)。帧就是数据链路层的"包裹",网络层丢过来的 IP 数据报,会被整个装进帧的"数据"区域里,外面再套上快递单(头部)和验货签(校验尾)。
最常用的以太网 II 帧(Ethernet II,也叫 DIX 帧)格式长下面这样:
以太网 II 帧(Ethernet II)整体结构
┌──────────┬────────┬──────────────┬──────────────┬──────┬────────────────────────┬──────────┐
│ 前导码 │ 帧定界 │ 目的 MAC 地址│ 源 MAC 地址 │ 类型 │ 数据 │ FCS │
│ Preamble │ SFD │ Destination │ Source │ Type │ Payload │ CRC 校验 │
│ 7 字节 │ 1 字节 │ 6 字节 │ 6 字节 │ 2字节 │ 46 ~ 1500 字节(含填充)│ 4 字节 │
└──────────┴────────┴──────────────┴──────────────┴──────┴────────────────────────┴──────────┘
│可选的同步信息│ │ ↑ 这三块被称为"帧头" │ │ │
└── 接收方并不把它算作帧本体(更严谨地说,帧本体从目的地址开始) 🡕 从"目的地址"
到"数据"这一整段,都被 FCS 覆盖
我们逐个字段拆开看,每一个都有讲究:
- 前导码(Preamble,7 字节):一串固定模式的"1/0 交替"信号,作用是给接收方的网卡做时钟同步,告诉它"数据要来了,准备好接收"。它像运动会起跑前裁判喊的"各就各位"。注意,接收方在计算帧长度时,通常会把这 7 字节加上紧随其后的一字节"帧首定界符"(SFD,Start Frame Delimiter,帧首定界符,固定
10101011)看作"同步用的前导信息",不算进帧的正式组成部分——正是这一个字节的"特殊结尾"标记了真正的帧从下一字节开始。 - 目的 MAC 地址(6 字节):这一帧要发给谁。如果填的是
FF:FF:FF:FF:FF:FF,那就是广播帧,发给网段上所有人(后面讲广播域和 ARP 会用到)。 - 源 MAC 地址(6 字节):这一帧从哪个网卡发出的。它由发送时自动填上,就是发送方自己的硬件地址。
- 类型/长度字段(Type/Length,2 字节):这里有个历史渊源。Ethernet II 用这个字段表示"上层协议类型":
0x0800表示上层是 IP,0x0806表示上层是 ARP,0x8035表示上层是 RARP,0x8100表示这是个带 VLAN 标签的帧(VLAN 一节会讲)。而 IEEE 802.3 曾经用它表示"后面数据的字节长度"。接收方怎么区分是"类型"还是"长度"呢?靠一个阈值:如果字段值大于等于0x0600(十进制 1536),就当它是类型;否则就当它是长度。 - 数据(Payload,46 ~ 1500 字节):这是网络层(IP 数据报、ARP 报文等)装进来的"货"。它有一个下限 46 字节、一个上限 1500 字节。等一下,为什么还有下限?因为帧太短是不允许的,这个 46 字节的下限是为了保证整帧长度不会低于以太网的最小帧长(14 字节帧头 + 46 字节数据 + 4 字节 FCS = 64 字节),而这 64 字节的最小帧长又和碰撞检测机制强相关(碰撞域一节会展开)。如果上层数据不足 46 字节,就会在末尾补填充位(Padding)凑够;这个被补进来的填充规范地叫"补白"。数据超过 1500 字节怎么办?那就必须让网络层去"分片"(MTU 一节会展开)。
- FCS(帧校验序列,4 字节):这是整帧的"防伪码"。发送方用 CRC-32(循环冗余校验,一种通用的差错检测算法)对"从目的地址到数据(含填充)"这一整段算出一个 32 位的校验和,附在帧尾。接收方用同样的算法重新算一遍,若对不上,就说明这一帧在路上被改坏了,直接丢弃。这一层提供的是"检错"而不是"纠错"——它只负责发现问题,然后把问题帧扔掉,而不负责把它修好。想修好得靠上层的 TCP 重传。
这里顺带讲一个很多人会搞混的点:FCS 到底覆盖哪些字节? 答案是从"目的 MAC 地址"开始,一直到"数据区"结尾(含填充),一共 14 字节帧头加数据。前导码和帧定界符不在校验范围内,因为它们只是同步用的"前导哨语"。记住这条,抓包看 CRC 错误位置时你就不会丈二和尚。
关于以太网帧还有几个高频"坑",提前帮你踩平:
- 最大帧长不是 1518 吗,怎么 MTU 是 1500? 1500 是"数据区"最多能装多少,也就是 MTU。而一个满额的以太网 II 帧,头 14 + 数据 1500 + FCS 4 = 1518 字节。这两个数别搞混:MTU 说的是能装的货,1518 说的是装满后的整个包裹尺寸。
- 为什么最小帧长是 64 字节? 这要追溯到共享介质年代。因为发送方用自己的信号还没传完时,如果和最远处的发送方发生了"碰撞",它能立刻发现并重发;如果帧太短、一下就发完了,碰撞信号还没传回来,发送方就"误以为发成功了"。64 字节正是为"在碰撞信号回来前,帧至少还没发完"而定的经验值。
- 现代以太网常常开 Jumbo Frame(巨型帧):在交换机等设备全部支持并统一配置的前提下,可以把 MTU 从 1500 放大到 9000 甚至更大,减少大传输的碎片开销。但注意,必须整条链路上所有设备都认,否则一个设备拆一个不拆,反而会出错。这是生产环境里一把锋利的双刃剑。
认识 MAC 地址
帧头里最显眼的,就是那两个各 6 字节的 MAC 地址(MAC,Media Access Control,媒体访问控制地址,也叫硬件地址或物理地址)。你必须认识它,它是数据链路层的"门牌号"。
MAC 地址的作用一句话:用来识别数据链路层中相连的节点。它就像每个网卡的独特姓名,设备之间在同一个数据链路里呼应时,靠的就是它。
它的几个硬属性要记牢:
- 长度为 48 位,也就是 6 个字节。48 位拆成 6 个字节,写作 6 组由冒号分隔的十六进制数,例如
08:00:27:03:fb:19。 - 一般在网卡出厂时就已经确定,固化在硬件里,所以叫"物理地址"。理论上每个 MAC 地址全球唯一。
- 正常情况下不可修改,但有几个例外要注意:一是虚拟机的 MAC 地址不是真实的网卡地址,是由虚拟化软件模拟出来的,存在冲突的可能(两个虚拟机克隆自同一镜像时最常撞车);二是部分网卡(尤其是某些服务器网卡和 Linux 下用
ip link set)支持用户自定义 MAC 地址,也就是"MAC 地址可以手工改,但改了不一定能上网",因为很多证书、认证、白名单系统都绑定了固定 MAC。
另外,前 3 个字节(OUI,Organizationally Unique Identifier,组织唯一标识符)是由 IEEE 统一分配给各网络设备厂商的,后 3 个字节是厂商自己内部编号。所以看到 08:00:27 你大概能猜到是某家虚拟化厂商的产品——00:0C:29、00:50:56 是 VMware,08:00:27 是 VirtualBox,52:54:00 是 QEMU/KVM 的常见前缀。这招在排查虚拟化环境时特别好用。
最后提一个容易忽略的细节:48 位里的最低位(也就是第一个字节的最低位)是一个特殊标记位,叫 I/G 位(Individual/Group,单播/组播位)。它为 0 表示这是单播地址(给一个特定设备),为 1 表示这是组播/广播地址。比如广播地址 FF:FF:FF:FF:FF:FF 的第一个字节 FF 最低位是 1,正因为它带了这个标志,所有网卡才知道"这是发给所有人的,别丢弃"。
再次搬出白龙马:MAC 与 IP 的分工
你可能已经隐隐觉得,MAC 和 IP 长得有点"像",都像一串能定位的地址。那它俩到底什么关系?为什么明明有 IP,还必须要有 MAC?这里我们把"白龙马"比喻再升级成一张对照表,一次说透:
| 对比维度 | IP 地址 | MAC 地址 |
|---|---|---|
| 作用范围 | 全局,定义整个路途的起点和终点 | 局部,定义当前这一段链路的起点和终点 |
| 词汇 | Internet 层的"逻辑地址" | 数据链路层的"物理地址" |
| 长度/形态 | IPv4 32 位,点分十进制 | 48 位,冒号分隔的十六进制 |
| 分配方式 | 由网络管理员或 DHCP 动态/静态分配 | 出厂固化(通常) |
| 是否随设备迁移而变 | 变,换网络就换 | 不变,跟着网卡走 |
| 是否可路由 | 是,路由器只认 IP | 否,路由器不按 MAC 路由 |
| 在本段链路上会不会改写 | 永远不变 | 每一跳都会被改写为下一段的收发方 |
看完这张表,你就明白那句最有名的话了:"IP 找得到远方的位置,MAC 跨得下眼前的这段路。" 数据包在网络里像一个接力棒,在每一段链路上,接力棒都被换上"这一段起点 MAC → 这一段终点 MAC"的新标签,但标签里的 IP 始终是同一个,因为那是它最终的归宿。
顺带说一句:正因为路由器按 IP 转发、不按 MAC 转发,所以 MAC 地址只在"同一个数据链路(同一个广播域/网段)"内部有意义。跨网段的包,在每一跳都重新标 MAC。这条结论后面讲交换机、ARP 时还会反复用到。
数据链路层到底要干哪几件事
上面我们把"职责"列过一次,这里再把它放大成一张"工作清单",因为它是理解整个数据链路层的行为学基础:
- 封装成帧(Framing):把上层数据包成"帧",加上头部(地址/类型)和尾部(校验),相当于把快递装进贴好面单的纸箱。
- 物理寻址(Physical Addressing):用 MAC 地址标明发送方和接收方,让每个接口都能回答"这包裹是不是给我的"。
- 差错检验(Error Control):用 FCS 检测帧在传输中是否被破坏,破坏就丢弃,不负责修复。
- 介质访问控制(MAC 子层真正的正事):在多个节点共享一根网线(同一冲突域)时,决定"谁能占线说话",典型机制就是以太网的 CSMA/CD。
- 流量控制与可靠性(可选):某些链路协议(如 HDLC、PPP 的可靠模式)提供逐链路的确认与重传,但这在以太网里通常不做——以太网把这活儿完全甩给了 TCP。
记住这条总体哲学:数据链路层的可靠性是"尽力而为 + 检查错误",真正的"保证送达"还得靠传输层。 它只保证"送到且检测出坏包",不保证"坏包会补发"。
交换机与碰撞域
知道帧长什么样、字段含义了,接下来要看"帧到底怎么在设备之间传递"。这就要讲到网络里那个被称作"小平板"的神器——交换机(Switch)。但在讲交换机之前,我们必须先回到它的前身,因为那段历史直接解释了很多"为什么"。
早期的以太网设备是一个叫集线器(Hub)的东西。注意关键区别:集线器虽然外形上连接成星型,但它在逻辑上还是"一根共享的总线"。什么意思?就是所有端口实际共享同一根通信介质,任何一个时刻只允许一台机器"说话",如果两台机器在同一时刻往介质上发数据,信号会撞在一起、互相干扰,这就是碰撞(Collision)。
于是以太网发明了一套避免碰撞的规则,叫 CSMA/CD(Carrier Sense Multiple Access with Collision Detection,载波监听多路访问/冲突检测)。它的工作过程很像一群人围着一张桌子轮流发言:
- 先听(Carrier Sense):发之前先侦听介质上有没有人在说话,没人说才开始发;
- 一起说就会撞(Collision):如果两台机器几乎同时开始发言,就发生碰撞;
- 发现撞了就退让:碰撞后双方随机等待一个时间再重试(这里的退避算法叫二进制指数退避,等待时间随重试次数指数翻倍,避免大家又同时重发)。
被 CSMA/CD 约束的这一群设备,互相干扰、共享一根介质的范围,就叫碰撞域(Collision Domain,碰撞域)。碰撞域越大,设备越多,碰撞概率越高,有效带宽就越低。这是集线器时代最头疼的问题——所有人挤在一根"大喇叭"上抢麦。
而**交换机(Switch,交换式集线器)**的诞生,正是为了解决这个痛点。交换机本质上是一种"智能多端口转发设备",它和集线器最本质的区别是:
- 集线器是"广播复制":收到的任何信号原样复制给所有端口。所有端口共享带宽,处在同一个碰撞域。
- 交换机是"定向转发":它通过学习知道每个端口后面连着哪台机器,把帧只发给目标所在的那个端口。这样一来,每个端口独享带宽,每个端口都是自己独立的碰撞域。
用一张简陋的图来表达这种差异:
集线器(Hub):一根共享大喇叭,所有人都要抢麦(同一碰撞域)
A ──当前──┐
╔═══════════════╗
║ HUB ║←── 进来的信号,复制给所有其它端口
╚═══════════════╝
B ──────────┘ C ──────────┘
当 A 在发时,B、C 都必须"闭嘴",否则碰撞
交换机(Switch):各端口自成一路,定向投递(每端口独立碰撞域)
A ──┐
╔═══════════════╗
║ Switch ║←── 只把发给 B 的帧,送到连着 B 的那个端口
╚═══════════════╝
B ──┘ C ──┘
A 和 C 可以同时收发,互不冲突
所以结论很干净:交换机的出现,把"碰撞域"从"整个设备的全部端口"缩小到了"单个端口",从而大幅提升了以太网的实际可用带宽。现代交换机几乎都是"全双工 + 每端口独立碰撞域",CSMA/CD 在这类端口上其实已经很少真正发生作用了,但它作为"共享介质时代"的逻辑仍然要懂。
一个小小的概念澄清:交换机的学名叫"网桥(Bridge)"的高级形态——网桥指连接两个网段、根据 MAC 地址转发帧的二层设备,交换机就是多端口、高性能的网桥。"二层"说的就是数据链路层(OSI 模型里数据链路层在第 2 层),所以交换机又叫"二层交换设备(L2 Switch)",它只认 MAC,完全不知道 IP 的存在。
广播域:一次广播能传播多远
和碰撞域容易混在一起的一个概念,是广播域(Broadcast Domain)。两者必须分清:
- 碰撞域:以"传输会互相干扰"来划分的物理范围;
- 广播域:以"广播帧能把多少人吵醒"来划分的逻辑范围。
网卡接收帧时有个铁律:帧头里的目的 MAC 如果不是自己、也不是组播/广播,那就直接丢弃。那么"发给所有人的广播帧"(目的 MAC = FF:FF:FF:FF:FF:FF)会怎样?
- 交换机:收到广播帧,会往除来源端口以外的所有端口转发(这叫泛洪 Flooding)。所以交换机不会隔离广播域——一个交换机连接的所有端口,处于同一个广播域内。
- 路由器:路由器是三层设备,默认不转发广播帧。于是路由器是广播域的天然边界——广播到路由器为止,不会跨路由。
这就得到一个实用结论:在一个局域网里,广播域越大,广播风暴的影响面就越大。 一台机器一广播,全网的人都得停下手里的事听一耳朵。真正把广播域"隔开"的,一是路由器(用 IP 子网划分),二是后面要讲的 VLAN(在同一台交换机内逻辑上隔开)。
你可能要问:广播到底有什么用,值得这么大动干戈?用处非常大——正是靠广播,ARP 才能在不知道目标 MAC 的情况下找到它。 这是下一节我们随手就能见到的例子。
交换机的 MAC 地址表是怎么"学"出来的
我们已经知道交换机会"把帧定向发给目标所在端口",但它怎么知道"目标在哪端口"?答案近乎顶神奇:它根本不用别人告诉它,它靠自己"看"就学会了。这个看家本领叫 MAC 地址表学习(MAC Address Learning,MAC 地址转发表学习)。
交换机内部维护一张表,长得像这样(动态 表示是学来的,会老化;静态 表示管理员手动配置的,不会老化,生产环境常见于做端口与 MAC 的绑定):
交换机 MAC 地址表(示意)
┌──────────────────┬────────┬────────┬────────┐
│ MAC │ 端口 │ 状态 │ 老化时间 │
├──────────────────┼────────┼────────┼────────┤
│ 00:1a:2b:3c:4d:5e│ Port 1│ 动态 │ 300s │
│ 08:00:27:03:fb:19│ Port 5│ 动态 │ 300s │
└──────────────────┴────────┴────────┴────────┘
它学习的完整流程,可以概括为"查自己、学他人、泛洪未知、老化过期"四步。我们用"主机 A 给主机 B 发第一帧"来演示一遍:
- 学他人(Learning):交换机收到从 Port 1 进来的这一帧,看到源 MAC 是 A,于是在表里记一笔"MAC A 在 Port 1"。这样以后发给 A 的帧就不用广播了。注意,交换机是从"来源"学"来源 MAC",绝不会学"目的 MAC"。
- 查自己(Searching):接着它看帧的目的 MAC 是 B,去表里查,发现表里没有 B 的记录。
- 泛洪未知(Flooding):目的地址没查到,就是"未知单播",交换机只会一招——除了 Port 1 之外的所有端口都转发一遍。B 收到后发现是给自己的,于是回包。
- 再学习(也学对方):B 的回包从 Port 5 进来,交换机又学到"MAC B 在 Port 5"。这下两张表都齐了,之后 A→B 的帧就能精确投递,不再泛洪。
等表项成熟后,转发就是"查表 + 定向发送",十分高效。但有两条硬规则连同着几个不可不防的坑:
- 表项不是永久的,需要老化(Aging):交换机给每个动态表项设了一个老化时间,常见默认约 300 秒(5 分钟)。如果 5 分钟内在某个端口再没见过该 MAC,这条就删掉。为什么?因为设备可能会被拔走下移到别的端口、更换网卡、或干脆关机下线,留着过期表项反而会转发错。这与 ARP 缓存老化是同一个哲学(下面 ARP 一节再讲)。
- 端口漂移(Port Flapping / MAC Move):如果你把同一台机器从交换机上换个口插,它的 MAC 在某端口"呼呼出现"又在别的端口"出现",交换机会反复改写学习表、狂发广播,这可能是物理挪动、环路、甚至 MAC 欺骗攻击的信号。排查时看到某个 MAC 在多个端口间跳来跳去,多半是搭了个环路或用机顶盒接回了自己的线。
- 关于环路:交换机天然不防环路。一旦两交换机之间接了两条线形成环路,未知单播和广播就会被来回放大复制,很快泛洪到全网瘫痪——这就是传说中的广播风暴。生产环境解决它要靠 STP(生成树协议,Spanning Tree Protocol,生成树协议)主动掐断冗余链路。这一课通常单独讲,这里先记下"交换机不会天然避免回路"这个结论。
VLAN:给广播域"画格子"
前面说广播域的边界要么是路由器、要么是 VLAN。现在我们把 VLAN 这个概念落地。VLAN(Virtual LAN,虚拟局域网)是用软件把一台物理交换机,从逻辑上切分成多个互不相通的"虚拟交换机"。它的最大价值之一,就是在不需要额外加路由器的情况下,把广播域在交换机内部划分成多个。
怎么"切"的?靠的是在帧头里插一个 4 字节的标签(Tag)。承载这种标签的协议叫 IEEE 802.1Q,它把原来的"类型字段"改成 0x8100(表示"我后面跟着一个 VLAN 标签"),并在标签里放一个 12 位的 VLAN ID——最多可以表示 0~4095 共 4096 个编号。带标签的帧格式大致如下:
带 802.1Q 标签的以太网帧(在 源MAC 和 类型 之间多了一块):
┌────────┬──────────┬────────┬─────────┬──────────┬──────────────────────┬──────────┐
│ 目的MAC│ 源MAC │Tag Type│ VLAN ID │ 类型 Type │ 数据 │ FCS │
│ 6 字节 │ 6 字节 │0x8100 │ 12 位 │ 0x0800… │ 46 ~ 1500 字节 │ 4 字节 │
│ │ │ 2 字节 │ (含PCP/DEI) │ 2 字节 │ │ │
└────────┴──────────┴────────┴─────────┴──────────┴──────────────────────┴──────────┘
└────────── 整体这 4 字节就是 802.1Q 标签 ──────────┘
VLAN 带来的好处非常实在:
- 隔离广播域:不同 VLAN 之间广播互不相通,一台主机的广播不会吵到别的 VLAN,广播风暴的爆炸半径大幅缩小,安全性也上来(不同部门逻辑上隔离)。
- 按需划网:同一个物理交换机上,研发部、财务部各用各的 VLAN,甚至透传下一层也可以在同一根物理线上跑多个 VLAN。
- 跨交换机(Trunk,中继):两台交换机之间如果想让多个 VLAN 都走过同一根线,就把这根线设成 Trunk 口,让它传输带标签的帧;接主机的那边通常是不带标签的 Access 口(接入端口),标签在出口剥掉。
关于 VLAN 和 MTU,有个常被忽略的点值得记下:802.1Q 帧在"逻辑上"比标准以太网帧多了 4 字节,但通常这 4 字节不算进 MTU(因为 MTU 统计的是 IP 数据报能装多少,IP 并不变)。可别小看这层,真要在带 TRUNK 的链路上做巨型帧排障时,这条"标签占位"常常是隐藏的加难题。记得:MTU 的 1500 指的是"数据(IP 数据报)"的容量,不是整个电信号帧的裸长度。
认识 MTU
接下来是这个系列的"老朋友",也是今天第一个真正和数据链路强绑定的"坑王"——MTU(Maximum Transmission Unit,最大传输单元)。
一句话定义:MTU 相当于发快递时对包裹尺寸的限制,而这个限制是"不同的数据链路对应的物理层"产生的限制。 每种链路类型自己定一个"我最长能一口气装多少数据",这就像各家快递公司对单个包裹的最大尺寸各有规定。
以太网的规定我们已经见过:数据区最小 46 字节、最大 1500 字节,其中最大值 1500 就叫以太网的 MTU。注意两点:
- 不同的网络类型,MTU 完全不同。以太网是 1500,普通 PPPoE 拨号是 1492(1500 少了 8 字节 PPPoE 头),FDDI 是 4352,令牌环是 4464,而 Linux 的回环接口 loopback 的 MTU 通常是 65536(因为它是纯软件接口,不受物理介质限制,爱装多大装多大)。
- 如果数据包超过当前链路的 MTU 怎么办? 那就必须分片(Fragmentation)——把一个大包裹拆成多个不超过 MTU 的小包裹再发。比如一个数据包要从以太网路由到拨号链路(MTU 更小),就得在路由器上按更小的 MTU 拆开。
理解 MTU 的钥匙是:它是一切"包太大"问题的总根源。 网络层、UDP、TCP 的故事,其实全都是围着"1500 这个天花板"打转。接下来我们就一层一层看 MTU 如何影响 IP、UDP 和 TCP——这也是刷题和面试最爱考的一段。
MTU 对 IP 协议的影响:分片与重组
由于数据链路层 MTU 的限制,对于较大的 IP 数据包必须进行分包(分片)。IP 层分片有一套极其标准的机制,需要掰开揉碎:
- 把较大的 IP 包分成多个小包:每个小包都改成"一个带 IP 头的完整 IP 数据报"(IP 头本身就是每个分片都要带一份的),因为每个分片都必须能独立在网络里走。
- 给每个小包打上相同的"族徽":IP 头的16 位标识(Identification, id)字段,同一批分片必须完全相同。接收方就靠"id 相同"知道自己手里这批小包是"一家人"。
- 用标志字段表示"能不能拆、拆到哪完":IP 头里有 3 位标志(Flags)字段,其中真正干活的是后两位:
- DF(第 1 位有效值,Don't Fragment,不分片标志):置 1 表示"我不同意分片",如果路上某个设备的 MTU 装不下,就直接丢弃并回一个 ICMP 报错,而不是硬拆——为后面讲"PMTU 黑洞"埋个伏笔;
- MF(第 2 位有效值,More Fragments,还有分片标志):置 1 表示"这还不是最后一片,后面还有",置 0 表示"这是最后一片"。注意方向——MF=1 表示"还有更多",MF=0 表示"到此为止"。
- 另外还有一个 13 位分片偏移(Fragment Offset)字段,以 8 字节为单位记录"这一片在原数据里的偏移位置",接收方据此确定每片该拼回哪里。
- 接收端重组:到达对端后,接收方的 IP 层把这些小包按"偏移 + MF 标志"的正确顺序重新拼装,再交还给上层(传输层)。这叫作重组(Reassembly)。
重点来了,这里藏着一个最经典的"坑":
- 一旦这些小包里有任意一个丢包,接收端的重组就会失败,整个这个"被拆掉的原始大包"就废了。
- 更关键的是:IP 层不会负责重新传输数据。它只负责尽力转发,丢了就丢了,重传是 TCP 的活儿;还没有 4.6 层的 UDP 就真的彻底凉了。
所以分片是"能省则省"的重型操作——分片带来的第一个坏处是"任何一个分片都会拖累整包",第二个坏处是"路由器处理分片有额外开销,而且丢包率随分片数上升"。这就是为什么 TCP 要想尽一切办法在源头就避免分片——它宁可把大发给不看 MTU 的 UDP 去踩,也绝不轻易给自己挖"分片"的坑。
MTU 对 UDP 协议的影响
我们回顾一下 UDP:它本身就是"无连接、不保证到达"的传输协议。当 MTU 插进来,问题雪上加霜。
- 如果一条 UDP 数据报携带的数据超过 1472 字节(即
1500 - 20 - 8:1500 是数据链路层 MTU,减去 IP 头 20 字节,再减去 UDP 头 8 字节),那么它就会在 IP 层被拆分成多个 IP 数据报(分片)。 - 这多个 IP 分片里,任意一个丢失,都会引起接收端 IP 层重组失败,于是整条 UDP 数据报等于白发了。
这意味着什么?如果 UDP 数据报在网络层被分片,整条数据被丢失的概率就大大增加了。用数学感觉来说:假设单个分片的丢失概率是 p,一个被拆成 n 片的大包,丢包概率直接变成 1 - (1-p)^n,n 越大越惨。这就是 UDP 在传输大块数据(比如某些多媒体、某些 DNS over UDP 的应答、或手写的大 UDP 报文)时容易"整包消失"的根本原因:不是收不到,而是重组残缺、直接被扔。
实操上的建议是:写 UDP 程序时,尽量把单个数据报控制在不触发 IP 分片的大小内(对普通以太网就是 ≤ 1472 的"性命线")。 如果确实要传大块数据又担心丢,那就应该走 TCP,或者自己实现分段与重传。这条 1472 的经验值,面试官特别爱考,务必背住它以及它背后的减法从何而来。
MTU 对 TCP 协议的影响:MSS 协商
UDP 在分片上吃尽苦头,TCP 决定"我在源头就不让分片发生"——手段就是 MSS(Maximum Segment Size,最大报文段长度)。
先明确:TCP 的一个数据报(段)也不能无限大,它同样受制于 MTU。所谓 MSS,就是 TCP 单个数据段(一个 TCP 报文)最多能携带多少字节的"应用数据"——注意,MSS 不含 TCP 头、不含 IP 头,它只统计"装应用数据的部分"。
MSS 是怎么防止分片的?核心机制是在建连阶段双方协商:
- 双方发送 SYN 时,各自在 TCP 头部的一个选项(Option)里写上自己支持的 MSS 值。这个选项的 Kind(种类)=2,长度 4 字节,正是源材料里提的"TCP 首部的 40 字节变长选项(kind=2)"里承担"MSS 协商"的那一个。
- 双方得知对方的 MSS 值之后,选择较小的一方作为最终 MSS。为什么要取小?因为一条 TCP 连接要经过好多链路,任一段都不能超过它的 MTU;取保守的"两边都装得下"才是安全的。通常以太网链路上两端的 MSS 就是
1500 - 20(IP 头) - 20(TCP 头) = 1460字节(这里假设两边 TCP 头都不带额外选项;一旦开了窗口缩放等选项,TCP 头可能是 24 字节甚至更多,MSS 会相应减小,实际上交换双方依据对端网口配置的 MTU 算)。 - 因为发送方每段都保证不超过 MSS,而这些 MSS 又按"不超过路径上的 MTU"精心选了,所以 TCP 数据段在 IP 层就天然不会被分片——这是 TCP 避坑 UDP 那口锅的根本原因。
MSS 和 MTU 的关系,可以浓缩成一张表,面试会考,务必记牢:
| 链路类型 | 二层 MTU | IP 头 | TCP 头 | 对应的 MSS(应用数据) | 备注 |
|---|---|---|---|---|---|
| 普通以太网 | 1500 | 20 | 20 | 1460 | 最常见的经典值 |
| 以太网 + 802.1Q | 1500(IP 不变) | 20 | 20 | 1460 | 标签通常不进 MTU |
| PPPoE 宽带拨号 | 1492 | 20 | 20 | 1452 | 少了 PPPoE 头 8 字节 |
| 巨型帧 Jumbo | 9000 | 20 | 20 | 8960 | 需全链路支持 |
还有一个与 MSS、MTU 联动的高频"坑":MTU 黑洞。设想一条链路中间某个设备的 MTU 变小(比如 NAT、VPN、错误配置),但网络设备又悄悄丢弃"带 DF 标志的、装不下的大包"而不回 ICMP 报错(很多防火墙默认禁 ICMP 或处理不当),那么 TCP 就会反复超时重传却永远发不过去——这就是著名的 PMTU(路径 MTU 探测)黑洞现象。排查它最经典的办法就是上面 IP 分片一节提到的:抓包看有没有 ICMP "Fragmentation Needed(需要分片但 DF=1)"报错。
结尾还是那句话:"TCP 小口慢咽,UDP 大口贪吃。" MSS 机制让 TCP 从一开始就把每小口装在安全尺寸以内,从根源上绕开了分片重组的大坑。
用 ifconfig 查看硬件地址和 MTU
理论讲了一大堆,该落到实践了。在 Linux 上想一次性看到这台机器的 IP 地址、MAC 地址和 MTU,最经典的工具就是 ifconfig。
$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.100 netmask 255.255.255.0 broadcast 192.168.1.255
ether 08:00:27:03:fb:19 txqueuelen 1000 (Ethernet)
RX packets 123456 bytes 78901234 (78.9 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 654321 bytes 98765432 (98.7 MB)
TX errors 0 drop 0 coll 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
看懂这几行输出,你就同时复习了一遍这节课的所有主角:
ether 08:00:27:03:fb:19:MAC(硬件)地址,就是ether字段;mtu 1500:这块网卡所在以太网链路的 MTU;- 回环口
lo 的 mtu 65536:印证了我们前面说的"loopback 是纯软件接口,MTU 可以很大"; coll 0 / collisions 0:碰撞计数,反映的是 CSMA/CD 时代的碰撞统计,现代全双工端口一般都为 0。
要补充的是:ifconfig 出自 net-tools 包,如今在很多现代发行版里已经不是默认装的了,更现代、也更推荐的是 ip 命令族:
$ ip link show eth0 # 看第二层链路信息(含 MAC 与 mtu)
$ ip address show eth0 # 看 IP 地址(含 inet 与 MAC)
$ ip route show # 看路由表
对排查数据链路问题而言,我给你的建议是:先用 ip link 确认链路状态和 MAC/MTU,再用 arp -a 或 ip neigh 看邻居表,最后才去抓包。 三步走,九成链路故障都能水落石出。
ARP 协议:给 IP 找"门牌"MAC
现在,最后一个,也是今天最精彩的主角登场——ARP(Address Resolution Protocol,地址解析协议)。
先纠正一个重要误解:ARP 不是单纯的数据链路层协议,它更像一个"介于数据链路层和网络层之间"的协议——它能收到网络层(IP)和物理层(MAC)两边,专门负责在两者之间牵线。所以它既被数据链路层相关课程挂在嘴边,又不该被简单地归进某一层。
ARP 的作用一句话:它建立了主机 IP 地址和 MAC 地址的映射关系。
你可能要问:这映射关系有啥好建立的?直接告诉我不就行了?问题恰恰在于"告诉不了"。回忆开篇那句"应用只知道 IP 和端口,却不知道硬件的门牌":
- 在通讯时,源主机的应用程序知道目的主机的 IP 地址和端口号,却不知道目的主机的硬件(MAC)地址;
- 而底层接收的过程是数据包首先被网卡接收,网卡再去处理上层协议——也就是说,网卡收到的第一件事就是看"帧头里的目的 MAC 是不是我",如果不是就直接丢弃,根本没机会再去管里面的 IP;
- 所以,在真正发出数据之前,必须先拿到目的主机的 MAC 地址。可这 MAC 从哪来?答案就是 ARP。
用大白话讲,ARP 干的事就是:"我知道你叫 192.168.1.101(IP),但你住几栋几号房(MAC)?快告诉我。" 它扮演的是一个"DNS 的底层远房亲戚"——只是它解析的不是域名→IP,而是 IP→MAC。
ARP 的工作流程与缓存
那 ARP 具体怎么"要地址"?完整走一遍,你会理解 ARP 为什么依赖广播、又为什么需要缓存:
- 发请求(广播):源主机在本地网段上发出一个 ARP 请求,相当于站在走廊里大声喊:"IP 是谁 192.168.1.2 的?请把你的硬件地址告诉我。" 这一步要紧的是:这个请求是通过广播发出去的——把以太网帧头里的目的硬件地址填成
FF:FF:FF:FF:FF:FF,这样网段里每一台主机都能收到。 - 只有目标应答(单播):网段里每台主机都收到这个广播请求、拆开一看里头的"目标 IP",发现和自己不符的就沉默不语直接忽略;只有"目标 IP 就是我自己"的那台主机,会发一个 ARP 应答包给源主机,把自己的硬件地址填进应答包里。这一步是单播(只发给请求方,不再广播)。
- 建立缓存:源主机拿到目标 MAC 后,把它和目标的 IP 一起记进自己维护的 ARP 缓存表(ARP Cache),以后要发给这个 IP 就直接查表、不用再广播。
这里牵扯出两个极其经典的"想一想",源材料把它留作思考,我直接给答案:
想一想一:为什么要有 ARP 缓存表? 因为如果每次发数据都要广播一次"谁有 192.168.1.2 的 MAC",那整个网段里每台机器都要跟着"被吵醒"一次,广播风暴的压力就上来了。有了缓存表,只有第一次(或表项过期后)才广播,之后几十秒到几分钟内往同一目标发全是静默查表的本地操作——这是典型的"用空间(缓存)换时间(避免反复广播)"。
想一想二:为什么缓存表要有过期时间,而不是永远有效? 因为一切都在变:目标主机可能换了网卡(MAC 换了)、可能搬到别的端口/别的机器挂掉了、也可能被别的主机顶替了 IP。如果缓存永远有效,一旦表里的 MAC 已经"过气",发出去的帧就会被丢弃,而发送方永远蒙在鼓里。所以每条表项都设一个较短的有效期,过期后下次再发就重新走一遍"广播 + 应答"。源材料给的例子是"一般约 20 分钟",但这是教学常用说法,实际参数因系统而异:Linux 上邻居表(neighbor table,即 ARP 表)的超时由内核参数控制,常见的 base_reachable_time、gc_stale_time 往往只有几十秒到几分钟,可以去读/proc/sys/net/ipv4/neigh/* 下面的值核实——千万别背死"就 20 分钟"这个数。(此处为参照 Linux 常见默认值补充,不同发行版/内核版本可能不同。)
Linux 下查看 ARP 缓存表的方法是 arp -a,更现代的方式是 ip neigh:
$ arp -a
? (192.168.1.1) at 48:8f:5a:xx:xx:xx [ether] on eth0
? (192.168.1.2) at 08:00:27:03:fb:19 [ether] on eth0
$ ip neigh show
192.168.1.1 dev eth0 lladdr 48:8f:5a:xx:xx:xx REACHABLE
192.168.1.2 dev eth0 lladdr 08:00:27:03:fb:19 STALE
顺带提两个实战中常见的 ARP 坑:
- ARP 欺骗(ARP Spoofing):因为 ARP 应答不校验真实性,攻击者可以伪造"我就是 192.168.1.1"的应答,把别人的流量劫到自己这来。这是局域网内做中间人攻击最经典的一招,防御要靠静态 ARP 表或端口安全绑定。
- 免费 ARP(Gratuitous ARP):有些设备会在开机/换 IP 时主动发一个"我的 IP 和 MAC 是 X 和 Y"的广播(请求目标 IP 是自己),用于通知邻居更新缓存或检测 IP 冲突。你开不了机、开机就弹"IP 冲突",常常就是免费 ARP 在当场告状。
ARP 数据报格式
最后,我们把 ARP 报文长什么样彻底解剖一次。一个 ARP 报文,放在以太网帧的"数据"区里,整体(帧头 + ARP)如下:
完整的 ARP 报文在以太网帧中的布局
┌──────────┬──────────┬──────┬────────────────────────────────────────────────────┐
│ 目的 MAC │ 源 MAC │ 类型 │ ARP 报文(数据区) │
│ 6 字节 │ 6 字节 │0x0806│ │
└──────────┴──────────┴──────┴────────────────────────────────────────────────────┘
│
▼
ARP 报文内部字段(28 字节,固定长度):
┌──────────┬──────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┐
│ 硬件类型 │ 协议类型 │ 硬件 │ 协议 │ 操作码 │ 发送者 │ 发送者 │ 目标 │ 目标 │
│ HTYPE │ PROTO │ 地址长度│ 地址长度│ Opcode│ 硬件地址│ IP 地址│硬件地址│ IP 地址│
│ 2 字节 │ 2 字节 │ 1 字节 │ 1 字节 │ 2 字节 │ 6 字节 │ 4 字节 │ 6 字节 │ 4 字节 │
│ 1=以太网 │ 0x0800=IP │ 6 │ 4 │1=请求 │ =发送方 │ =发送方 │ =目标方 │ =目标方 │
│ │ │ │ │2=应答 │ 的MAC │ 的IP │ 的MAC │ 的IP │
└──────────┴──────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┘
逐字段解释,这些名字你可能在抓包里见过无数次:
- 硬件类型(HTYPE,2 字节):指当前链路层的网络类型,1 表示以太网。这样同一个 ARP 格式能复用在多种链路上。
- 协议类型(PROTO,2 字节):指要转换的"上层地址"类型,0x0800 表示 IP 地址。
- 硬件地址长度(HLEN,1 字节):对以太网地址来说是 6 字节。
- 协议地址长度(PLEN,1 字节):对 IP 地址来说是 4 字节。
- 操作码(Oper,2 字节):为 1 表示 ARP 请求(Op=1),为 2 表示 ARP 应答(Op=2)。接收方靠这个字段区分"你在问我还是你在回我"。
- 发送者硬件地址(Sender HA,6 字节):发送者 IP 地址(Sender IP,4 字节):请求者自己的 MAC 与 IP。
- 目标硬件地址(Target HA,6 字节):目标 IP 地址(Target IP,4 字节):把源地址填在 ARP 报文头里,在请求里"目标硬件地址"先填空(
00:00:00:00:00:00)等应答来填,目的是在应答里把这值填上;目标 IP 是请求的关键,应答者靠它判断"是不是在找我"。
这里有个非常容易让人皱眉的点,值得单独拿出来讲透:
"源 MAC 地址、目的 MAC 地址在以太网首部和 ARP 请求中各出现一次"——是不是冗余? 答案:对"链路层为以太网"的情况确实是多余的,但如果链路层换成其它类型的网络,就有可能是必要的。因为 ARP 报文的格式是"通用"的(靠前面的硬件类型/长度字段适配),而以太网的帧头天然能提供源/目的 MAC;可是像 FDDI、令牌环等链路在帧头里的寻址字段可能不能直接等价地放"该填的 MAC",所以 ARP 报文里索性把"发送者/目标硬件地址"再存一份,保证在任何链路上都能拿到地址信息。你就把它理解成:ARP 报文自带"保险的本地副本",不依赖某种特定帧头的长啥样。
用一次实际的抓包让你把这 28 字节全部对上号。假设你从 192.168.1.100 (MAC 08:00:27:03:fb:19) 第一次去 ping 192.168.1.1,那一刻会先出现一个 ARP 请求:
硬件类型=1(以太网) 协议类型=0x0800(IP) 硬件地址长度=6 协议地址长度=4
操作码=1(请求)
发送者硬件地址 = 08:00:27:03:fb:19
发送者IP = 192.168.1.100
目标硬件地址 = 00:00:00:00:00:00 ← 请求时还不知道,先空着
目标IP = 192.168.1.1 ← 关键!告诉全网 "我在找谁"
------ 这个请求被封装进以太网帧,帧头目的MAC是 FF:FF:FF:FF:FF:FF(广播)------
192.168.1.1 回应一个 ARP 应答(操作码=2,源 MAC 换成它自己的,填上自己的 MAC 单播回给 192.168.1.100)。此后两个 IP 之间再通信,就直接用这张缓存表里的映射,不再广播。你要是手边有 Wireshark,随便抓一次 ping 前的 ARP 广播,就能亲眼看到字段编号和我上面完全一致。
动手想一想与自测(附详解答案)
这一节不白讲,把上面所有知识点打散成几道题,就当一次自测。先自己做完再看答案,效果最好。
思考题 1:为什么一个 IP 包到了路由器上,路由器把源和目的 MAC 都换了,但 IP 却始终保持不变?
答:因为 IP 反映的是端到端的全局寻址(整个路途的起点和终点),它由发送方和接收方的 IP 决定,中间每经一跳都不需要改,改了反而没法路由到终点。而 MAC 反映的是每一段链路的局部寻址(这一段的起点和终点),数据一旦跨出当前网段进入下一段链路,这一段的路就走完了,帧头必须换上"新一段"的收发双方 MAC。这正是"白龙马"比喻的本质:接力棒(IP)的终点始终不变,而每一棒的白龙马(MAC)都在换。
思考题 2:一台主机的 ARP 缓存表项过期后,它要往同一个目标再发包,会发生什么?
答:表项过期后,该条目从缓存里被移除。下次再发时,发送方查表查不到映射,于是又在本地网段广播一条 ARP 请求,目标主机再单播应答,映射被重新学回来,数据包此时才能真正的物理寻址并发出。整个过程对应用层是不可见的(应用只是少等了一二百毫秒),但抓包看会多出一对 ARP 广播/应答。这也提醒我们:为什么频繁双向通信时网络很顺,一旦停了较久再发包,开头总会有一次常见 ARP 广播——就是这个道理。
思考题 3:交换机为什么要把"未知单播"(目的 MAC 在表里查不到)像广播一样泛洪给所有端口?
答:因为交换机只知道"源 MAC 在哪个端口"(这是它从入帧学来的),却不知道"目的 MAC 在哪个端口"。既然不知道,就不能盲猜某一个端口(猜错就丢帧)。以太网帧没有"尸检报告",只能采用最保守的策略:除了来源端口以外,所有端口都复制一份发出去,期望目标主机能收到并回复。而目标一旦回复,交换机就顺便从回复里学到了"目标 MAC → 端口",下次就能精确转发了。这个"先泛洪、学会后定向"的机制,正是 MAC 地址表学习能"从无到有"建起来的前提。
思考题 4:UDP 往网络里发了一个 3000 字节的 payload(假设网卡 MTU 1500、IP/TCP/UDP 头各 20/20/8,这里 UDP 只有 8 字节头),它会被拆成几片?哪一片丢了就整包作废?
答:这条 UDP 数据报长度 = 8(UDP 头)+ 3000 = 3008 字节的 IP 载荷能力。IP 层要按不超过链路 MTU 的尺寸去分片,同时每个分片还要预留自己的 IP 头 20 字节(和必要的 IP 选项空间,简化按 0)。所以能装进一个分片的"IP 载荷"上限是 1500 - 20 = 1480 字节。3008 里第一个分片装 1480,剩余 3008 - 1480 = 1528;第二个分片再装 1480,剩 1528 - 1480 = 48;第三片装最后 48。故会被拆成 3 个 IP 分片。而 UDP 没有重传、IP 也不管重传,任意一片(第一、第二还是第三)丢失,接收端 IP 层重组都会失败,整条 3000 字节的 UDP 数据报作废。这也是为什么"大 UDP"在网络里极易丢整包的根本原因——引申出一个万古不变的排障结论:UDP 负载一定要先扣掉 IP/TCP 头,控制在 1500-20-8=1472 以内,否则别怪丢包。
思考题 5:为什么 TCP 要和 MTU 较劲,把每段控制在 MSS 以内,而不是任由 IP 分片?
答:分片是"坏消息的放大器"——一旦被拆成 N 片,任意一片丢,原始整包就报废,而 IP 不负责重传,最后只能靠 TCP 重传整个段(而且大概率是重传整个原始段,不是只补一片,效率更低)。既是高概率丢包,又增加路由器处理开销。TCP 的做法是从源头绕开它:建连时双方交换各自的 MSS 选项(Options Kind=2),协商出"在双方链路 MTU 下都装得下"的小值,保证每个 TCP 段的长度在 IP 层永远不会触发分片。用一句话记:"由 TCP 控制小口慢咽,好过被 IP 强行腰斩。"
思考题 6:数据要经过一台 MTU 比发送端小的路由器,发送端的帧已经够小、不触发分片;但 SYN 里协商出来的 MSS 却是按发送端自己 1500 算的(如 1460),会发生什么?
答:这就是MTU 黑洞的典型成因。发送端以为路径 MTU 是 1500,SYS 协商出 MSS=1460,可中间某段实际 MTU 更小(比如只有 1400)。当这段大包带着 DF=1(TCP 数据段为防止分片几乎总是设置 DF)经过那个小而装不下的路由器时,路由器正常应该是"丢弃 + 回 ICMP Fragmentation Needed";但如果它或它背后的防火墙把 ICMP 屏蔽了,发送端就永远收不到"改小"的通知,于是反复超时重传、却始终无法成功建立大数据连接。排障手段:抓包确认有没有 ICMP "Fragmentation Needed";临时把某端 MTU 调小(比如 1400)再测;或关闭/调整 PMTU 探测(可因环境差异见仁见智,先以诊断为主)。这也是为什么"NAT/防火墙/虚拟化环境里 TCP 大包卡、小包却通"时,第一怀疑对象往往就是 MTU。
讲到这里,数据链路层最重要的一块拼图就齐了。回顾一下:我们从"两个设备之间传递"这个初心出发,认识了以太网这个定义"帧长什么样"的技术标准,拆开了以太网 II 帧的每一个字节,弄懂了 MAC 地址为什么是"局部门牌"、它和 IP 的"白龙马"式分工;接着搞清了交换机如何靠"学表 + 泛洪 + 老化"把碰撞域缩到单端口、广播域如何靠路由器和 VLAN 得到隔离;然后顺着 MTU 这条线索,把 IP 分片、UDP 那 1472 的性命线、TCP 的 MSS 协商一层层串了起来;最后用 ARP 给整条链路画上句号——IP 正是在它这里问出了 MAC 的门牌号,而交换机的运转又离不开 ARP 替它按需补全映射。
你现在应该拥有这样一张完整的心智地图:网络层告诉我"最终到哪去",数据链路层告诉我"眼前这一步怎么跨";IP 负责全程、MAC 负责逐跳;交换机在二层拿 MAC 定向投递,路由器在三层拿 IP 路由,而 VLAN 收拾广播域、MTU 锁死包裹上限、ARP 在 IP 与 MAC 之间做翻译。 这一层不再神秘,也不再是"听上去很玄"的黑洞——抓一次包,跑一遍 arp -a,再看一眼 ip link 的 mtu,你就亲手摸到了它的脉搏。
下一课我们继续往下走,从数据链路层迈向物理层,看看这台机器发出的那些"01 比特"究竟是怎么变成电信号、光信号,真正飞进网线里的。
还没有评论 — 第一条由你来留。