📚 第三章 数据链路
📄 一句话概括

数据链路层做两件事:把比特流切成帧,把帧送到「相邻的下一个设备」。 它不管「最终去哪」,只管「这一跳交给谁」——这是它与网络层的分工线。

📝 本节和前面几节的关系

前两章讲的是「网络是什么」,从这一章开始进入具体的一层。 数据链路层是最容易被忽略、又最容易被误会的一层——很多人把它和「物理连接」混为一谈。

一、这一层到底在做什么

一句话:把不可靠的比特流,变成一段一段可校验的帧。

它做的事 具体动作 不做的
成帧 在比特流里划出边界 不管边界里装的是什么
寻址 写清「这一跳交给谁」 不管最终目的地
差错检测 算 FCS,坏了就丢 不负责重传
介质访问 谁先说话、怎么排队 不管上层急不急

第二行的「寻址」是最容易搞错的一条:链路层的地址只用一跳,下一跳就换掉了。 所以「MAC 地址全球唯一」这件事,只在「出厂时」有意义——转发的时候它每一跳都在变。 第三行的「不负责重传」也很关键:以太网发现帧坏了,动作是「丢掉」,不是「要求重发」。 重传是 TCP 的活。 所以「链路层可靠」和「端到端可靠」是两件事,不能互相替代。

二、帧长是被谁规定的

帧不能太短,也不能太长,两头都有硬理由:

边界 数值 为什么
最小帧 64 字节 短于它,碰撞检测来不及生效
最大帧 1518 字节 长于它,一个站会占线太久

第一行的理由是「时间」:发送方必须在自己发完之前听到碰撞信号, 所以帧的发送时间不能短于「最远两端信号往返一轮」的时间。 10 Mbps 下这个往返是 51.2 微秒,正好是 64 字节。 第二行的理由是「公平」:共享介质上,一个站占线越久,别人等得越久。 1518 字节在 10 Mbps 下要占 1.2 毫秒——再长,语音这类实时业务就没法插进来了。 这两个数在今天都还在用:哪怕速率已经到 10 Gbps,「最小 64、最大 1518」这条规矩没变, 因为要兼容。 速率变了、场景变了,边界没变——这就是标准的惯性。

三、为什么它必须存在

假设没有这一层,会怎样:

没有链路层 后果
没有帧边界 接收方不知道一个包从哪开始、到哪结束
没有地址 总线上所有设备都收到,没法只发给一个
没有校验 一个比特翻了,可能被当成合法数据
没有访问控制 两个站同时发,信号叠成一团

第一行是最根本的:物理层给的是一条连续的比特流,没有「这是第几个字节」的概念。 要把它变成「一条条消息」,必须有一层来做切分——这一层就是数据链路层。 第三行那个「比特翻了」不是理论问题:铜线上一个干扰脉冲就能翻一位,光纤上 bias 漂移也能翻。 所以每一帧都带校验,坏的就丢。 这是用「丢」来换「不会把错误数据当正确的用」。 所以这一层的定位很清楚:它是「一段链路」的管家,不是「整条路径」的管家。 一段一段接起来,才成了端到端——这是下一章网络层要接手的活。

四、动手 Demo:数据链路层做了什么——看本机的平均帧长

判据很直接:操作系统同时统计「字节数」和「帧数」,两个数一除就是平均帧长。 如果链路层不存在「帧」这个切分,第二个计数就没有意义。

import psutil

c = psutil.net_io_counters(pernic=True)
for name in sorted(c):
    v = c[name]
    if v.packets_sent == 0:
        continue
    avg = v.bytes_sent / float(v.packets_sent)
    print('%s 发出 %d 字节 / %d 帧,平均 %.0f 字节'
          % (name, v.bytes_sent, v.packets_sent, avg))

实跑输出:

--------------------------------------------------------------------
数据链路层做了什么——看本机的平均帧长
--------------------------------------------------------------------
**数据链路层干的第一件事是「把比特流切成帧」,所以它的第一个可测量指标就是帧有多长。**
本机每张网卡的收发字节数与帧数都是现成的,两个数一除就是平均帧长。

  网卡     发送帧数     平均字节     接收帧数     平均字节
  ---------------------------------------------------------
  WLAN     3098363      1009         5862474      738

**全机累计发出 2.91 GB、收到 4.03 GB**(按 1024 进制算)。
**这个「平均字节」不是随便一个数,它被两条硬边界夹着**:**下界 64 字节**(以太网最小帧),
**上界 1518 字节**(14 头 + 1500 MTU + 4 校验)。**所以正常网卡的平均帧长永远落在这个区间里。**
**落在偏大那一侧说明「大包为主」**——**看视频、下载文件,帧都尽量装满 1500 字节的载荷;**
**落在偏小那一侧说明「小包多」**——**游戏、语音、大量 TCP 确认包,都会把平均值往下拽。**
**这张表还顺手证明了「帧」是真实存在的计量单位**:操作系统统计的不是字节,
是「字节」和「帧」两个独立计数——**如果链路层不存在「帧」这个切分,第二个计数就没有意义。**

本机只有 WLAN 有流量,平均帧长落在 1000 字节左右: 这个数被两条硬边界夹着——下界 64、上界 1518。 落在偏大那一侧,说明「大包为主」,典型的看视频、下载文件的流量结构。 「全机累计发出 2.88 GB」这个数 是用字节数除 1024 的三次方算的,不是除 1000。 存储厂商用 1000,操作系统用 1024,所以同一个硬盘在 Windows 里「容量变小」了——这是口径问题, 不是缩水。 这张表最有价值的地方是「两个计数同时存在」: 字节数和帧数是独立统计的,说明内核真的在数「帧」。 很多同学以为「帧」只是教材上的抽象概念, 其实它就是一个被计数的实体——驱动程序每发一帧,这个计数器就加一。

30 秒实验

  1. 把 packets_sent 换成 packets_recv,看收发的平均帧长差多少,想想为什么。
  2. 用 psutil.net_if_stats() 读 MTU,验证「最大帧 = MTU + 18」这个关系。
  3. 刷一个视频网站再看一次平均帧长,和刚才只开网页时比一比。

常见误区

  1. 以为数据链路层负责「把数据送到目的地」。 它只管一跳,最终去哪是网络层的事。
  2. 以为帧坏了会自动重传。 以太网只丢不补,重传是 TCP 的活。
  3. 以为 1518 这个上限是「技术做不到更长」。 是为了公平,让共享介质上的别人也能轮到。
自测三问(点开对答案) 1. 数据链路层做哪四件事? —— **成帧、寻址(一跳)、差错检测、介质访问控制。**它不负责重传,也不负责端到端。 2. 为什么最小帧是 64 字节? —— **因为发送方必须在发完之前听到碰撞信号。**10 Mbps 下「最远两端往返一轮」是 51.2 微秒, 正好对应 64 字节。 3. 「MAC 地址全球唯一」这句话哪里不准确? —— **唯一只在出厂时有意义。**转发时每一跳都会换掉,而且操作系统允许软件改写(MAC 克隆)。

下一节从「地址」开始:这一层靠什么认出「下一跳是谁」。

相关笔记