4. 以太网帧结构

各位同学,今天我们来聊聊以太网帧结构。说实话,这个知识点是网络编程的基石。你想想看,数据在网线上跑来跑去,总得有个规矩吧?以太网帧就是那个规矩。

我记得刚入行那会儿,总觉得帧结构就是背几个字段名。直到有一次,我在一个工业控制项目里排查丢包问题,抓包分析搞了三天,最后发现是FCS校验字段被某个老旧交换机截断了。嗯,从那以后,我再也不敢小看这个帧结构了。

4.1 以太网帧长什么样?

先给你看个直观的。一个标准的以太网帧,说白了就是下面这个结构:

+----------------+----------------+----------------+----------------+----------------+----------------+
|   前导码 (7B)   | 帧起始定界符(1B)|  目标MAC (6B)  |   源MAC (6B)   | 类型/长度 (2B) |   数据 (46-1500B)|
+----------------+----------------+----------------+----------------+----------------+----------------+
|                                                              |    FCS校验 (4B)   |
+--------------------------------------------------------------+------------------+

我习惯把这个结构分成三大部分来看:

  • 头部:前导码 + 帧起始定界符 + 目标MAC + 源MAC + 类型/长度
  • 载荷:数据部分
  • 尾部:FCS校验

为什么这么分?因为在实际编程中,我们处理头部和尾部的逻辑是完全不同的。头部用来寻址和识别,尾部用来纠错。

4.2 前导码和帧起始定界符

这两个字段你可能平时不太关注,但它们很重要。

前导码(Preamble):7个字节,每个字节都是0xAA(二进制10101010)。它的作用就是让接收端的时钟同步。你可以理解为,发送端在喊:“喂喂喂,我要发数据了,你准备好!”

帧起始定界符(SFD):1个字节,固定为0xAB(二进制10101011)。它标志着帧的真正开始。前导码的最后一位是0,SFD的最后一位是1,这个变化就是信号。

我的小技巧: 在写底层驱动时,我习惯把前导码和SFD合并处理。因为很多硬件芯片会自动生成这两个字段,你只需要关注后面的内容就行。但如果你在做FPGA开发,这两个字段就得自己拼了。

4.3 目标MAC和源MAC

这两个字段各占6个字节。MAC地址是网卡的物理地址,全球唯一。我建议你记住这个格式:

目标MAC:6字节,表示数据要发给谁
源MAC:6字节,表示数据是谁发的

这里有个坑。我曾经遇到过一个情况:某台设备的MAC地址是广播地址(FF:FF:FF:FF:FF:FF),结果整个局域网都收到了它的数据包。排查了半天,发现是网卡驱动配置错了。

注意: 目标MAC可以是单播、多播或广播地址。源MAC必须是单播地址,不能是广播或多播。这是IEEE 802.3标准规定的,别搞混了。

4.4 类型/长度字段

这个字段很有意思。它有两种用法:

  • 当值大于等于0x0600(1536)时:表示上层协议类型。比如0x0800是IPv4,0x0806是ARP,0x86DD是IPv6。
  • 当值小于等于0x05DC(1500)时:表示数据字段的长度。

为什么会这样?说白了,早期以太网标准用这个字段表示长度,后来发现不够用,就改成了类型。现在基本上都用类型了,长度由硬件自己算。

我个人习惯在抓包时重点关注这个字段。比如你看到0x0800,就知道这是个IP包;看到0x0806,就知道是ARP请求。这个判断在协议栈开发中非常常用。

4.5 数据字段

数据字段的范围是46到1500字节。为什么有下限?因为以太网要求帧的最小长度是64字节(从目标MAC到FCS)。减去头部和尾部,数据最少得46字节。

如果数据不够46字节怎么办?填充(Padding)。系统会自动在后面补0,直到凑够46字节。

我记得有一次写网络驱动,发现收到的数据包总是比发送的多几个字节。查了半天,原来是填充字段没处理好。嗯,这个细节很容易被忽略。

重要: 数据字段的最大长度是1500字节,这个值叫MTU(最大传输单元)。如果上层协议的数据超过1500字节,就需要分片。比如你在TCP里发一个2000字节的数据包,底层会自动拆成两个帧来发。

4.6 FCS校验字段

FCS(Frame Check Sequence)是帧尾部的4字节校验码。它用的是CRC32算法,对整个帧(从目标MAC到数据字段)进行计算。

接收端收到帧后,会重新计算CRC32,然后跟FCS字段比对。如果一致,说明数据没出错;如果不一致,直接丢弃。

我曾经在项目中遇到过一个问题:某条链路上偶尔出现CRC错误,但重传率不高。排查后发现是网线质量太差,信号衰减导致比特翻转。换了根好网线,问题就解决了。

避坑指南: 如果你在写协议栈,FCS的计算一定要用硬件加速。软件算CRC32太慢了,尤其是在高速网络环境下。我建议直接用网卡芯片自带的硬件CRC引擎。

4.7 帧结构总结

好了,我们来画个图,把整个帧结构串起来:

以太网帧结构全景图 前导码 7字节 SFD 1字节 目标MAC 6字节 源MAC 6字节 类型/ 长度 2字节 数据 46-1500字节 FCS 4字节 前导码+SFD 帧头部(14字节) 载荷 帧尾部 关键说明 • 最小帧长度:64字节(目标MAC到FCS) • 最大帧长度:1518字节(不含前导码和SFD) • 类型字段常见值:0x0800(IPv4)、0x0806(ARP)、0x86DD(IPv6) • FCS算法:CRC32,覆盖目标MAC到数据字段 数据不足46字节时,系统自动填充0 超过1500字节时,上层协议需要分片

这个图把整个帧结构拆得很清楚了。你想想看,从发送端到接收端,数据就是按照这个格式一层层封装和解封的。

最后说一句,FCS校验是保证数据完整性的最后一道防线。我在项目中见过太多因为CRC错误导致的数据损坏案例。所以,别偷懒,该做校验就做校验。

好了,以太网帧结构就讲到这里。记住这个结构,后面讲ARP、IP、TCP的时候,你就能理解它们是怎么封装在以太网帧里的了。

可可精品资料库