2. 物理层与数据链路层:以太网帧结构、MAC地址、CRC校验与丢包重传机制

说到网络连接稳定性,很多人第一反应是「信号好不好」。但在我眼里,真正决定连接是否靠谱的,其实是物理层和数据链路层这两兄弟。物理层管的是比特流怎么在线上跑,数据链路层管的是这些比特怎么组成帧、怎么纠错、怎么重传。说白了,没有这两层,上层协议再花哨也是空中楼阁。

我个人习惯把这两层合在一起看,因为它们在硬件和驱动里是紧密耦合的。今天我们就从以太网帧结构开始,一路聊到MAC地址、CRC校验,最后说说丢包重传那点事。

2.1 以太网帧结构:比特流的「集装箱」

数据在网络上传输,不是一股脑儿扔出去的。它得打包。这个包,在数据链路层就叫「帧」。以太网帧的结构,我做了这么多年协议分析,闭着眼都能画出来。

标准以太网帧(IEEE 802.3)结构:

+--------+--------+--------+--------+--------+--------+--------+--------+
| 前导码 | 定界符 | 目的MAC | 源MAC  | 长度/类型 | 数据载荷 | 填充  |  FCS  |
| 7字节  | 1字节  |  6字节  | 6字节  |  2字节   | 46-1500B | 可变  | 4字节 |
+--------+--------+--------+--------+--------+--------+--------+--------+

这里我重点说几个容易踩坑的地方:

  • 前导码和定界符:这8个字节在抓包时通常看不到,因为网卡在接收时会把它剥离。但它们是物理层同步用的,缺了不行。
  • MAC地址:6字节,全球唯一。但注意,唯一性只在理论上成立。我在项目里遇到过两次MAC地址冲突,都是小厂烧录时搞错了。
  • 长度/类型字段:当值小于1536(0x0600)时表示长度,大于等于1536时表示上层协议类型。比如0x0800就是IP协议。
  • 数据载荷:最小46字节,最大1500字节。如果上层数据不够46字节,就要填充。这个填充有时候会坑人——我曾经排查过一个bug,就是填充数据被误当成应用层数据解析了。
  • FCS:帧校验序列,4字节CRC32。这是保证数据完整性的最后一道防线。

经验之谈:抓包时如果看到「帧长度小于64字节」,别急着怀疑网卡坏了。很可能是填充没做好,或者你抓到了 runt frame(短帧)。我当年调试一个嵌入式设备,就是被这种短帧折腾了两天。

2.2 MAC地址:设备的「身份证」

MAC地址是数据链路层的核心标识。48位,通常写成六组十六进制数,比如 00:1A:2B:3C:4D:5E。前24位是OUI(组织唯一标识符),由IEEE分配;后24位是厂商自己分配的序列号。

但这里有个很多人忽略的点:MAC地址不是一成不变的。现代操作系统都支持MAC地址伪装(MAC spoofing)。我在做网络安全项目时,就经常用这个特性来测试网络设备的访问控制策略。

MAC地址类型 第1字节最低位 说明
单播地址 0 发给单个设备
多播地址 1 发给一组设备
广播地址 全1 FF:FF:FF:FF:FF:FF,发给所有设备

嗯,这里要注意:交换机转发帧的时候,就是靠MAC地址表来决策的。如果MAC地址表被搞乱了(比如ARP攻击),整个局域网都可能瘫痪。我曾经在客户现场遇到过这种情况,最后发现是一台中毒的PC在疯狂发送伪造MAC地址的帧。

2.3 CRC校验:数据完整性的「守门员」

CRC(循环冗余校验)是数据链路层保证数据不出错的核心机制。以太网用的是CRC32,生成多项式是:

G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1

这个多项式看着复杂,但硬件实现起来非常快。网卡在发送帧时计算CRC,把结果放在FCS字段;接收端重新计算,如果结果不一致,就说明帧在传输过程中被损坏了。

避坑指南:我曾经遇到过一个案例,某款网卡在特定温度下CRC计算会出错。排查了整整一周,最后发现是硬件设计缺陷——PCB走线太长导致信号时序偏移。所以,CRC校验通过不代表硬件没问题,它只能检测传输错误,不能检测计算错误。

CRC32的检错能力很强:

  • 能检测所有单比特错误
  • 能检测所有双比特错误
  • 能检测所有奇数个比特的错误
  • 能检测所有长度不超过32位的突发错误
  • 对于更长的突发错误,漏检率仅为 2^(-32)

说白了,CRC32在工程上已经足够可靠。但注意,它不能纠错——只能告诉你「数据坏了」,不能告诉你「哪里坏了」。纠错是上层协议的事。

2.4 丢包重传机制:数据链路层的「后悔药」

数据链路层本身不提供重传机制。你想想看,如果每帧都要确认重传,那效率太低了。实际上,丢包重传是上层协议(比如TCP)的工作。但数据链路层提供了一些辅助机制:

  1. 帧校验:通过CRC检测错误帧,直接丢弃
  2. 帧定界:通过前导码和定界符识别帧边界,防止错位
  3. 冲突检测(CSMA/CD):在半双工模式下检测冲突,等待随机时间后重发

这里我要特别说一下CSMA/CD。虽然现在全双工链路已经普及,但在一些老旧设备或特殊场景下,半双工仍然存在。CSMA/CD的工作流程是:

1. 监听信道
2. 如果空闲,发送数据
3. 如果忙,等待随机时间后重试
4. 发送过程中持续检测冲突
5. 如果检测到冲突,发送阻塞信号
6. 等待随机退避时间后重试
7. 重试次数超过上限(通常16次),报告上层

这个机制在10Mbps时代很管用,但到了100Mbps以上,冲突检测的窗口时间太短,所以全双工成了主流。全双工模式下,发送和接收各用一对线,不存在冲突问题。

核心观点:数据链路层的「丢包」其实分两种:一种是物理层传过来的比特流本身就错了(CRC校验失败),另一种是冲突导致帧被破坏。前者靠CRC检测,后者靠CSMA/CD处理。但无论哪种,数据链路层都不负责重传——它只负责「扔掉坏的」,重传是TCP的事。

2.5 知识体系总览

下面这张图是我自己整理的,把物理层和数据链路层的核心逻辑串起来了。你看一遍,应该能对本章内容有个整体把握。

物理层与数据链路层知识体系 物理层与数据链路层核心逻辑 物理层 比特流传输 信号编码/解码 介质访问控制 数据链路层 以太网帧封装 MAC地址寻址 CRC32校验 冲突检测(CSMA/CD) 关键理解: 1. 物理层负责「传比特」,数据链路层负责「组帧」 2. CRC校验只检测错误,不纠正错误 3. 数据链路层不负责重传,重传是TCP的工作

你看,物理层和数据链路层的关系,说白了就是「搬运工」和「打包员」的关系。物理层只管把比特从A搬到B,不管这些比特对不对;数据链路层负责打包、贴标签、检查包裹有没有破损。但包裹破了怎么办?数据链路层说「我不管,我只管扔」,然后TCP在楼上骂骂咧咧地重新发一份。

这就是为什么连接稳定性不能只看一层。物理层信号不好,数据链路层CRC校验失败率就高,上层TCP就得频繁重传。你优化一万遍TCP参数,不如先把网线换一根。

实战建议:排查网络不稳定问题时,先看物理层(信号强度、误码率),再看数据链路层(CRC错误计数、冲突计数)。我一般用 ethtool -S eth0 看网卡统计信息,如果 crc_errors 或 collisions 持续增长,那问题大概率出在物理层或数据链路层,别急着调TCP参数。