目录
- 一、数据链路层的功能
- 二、组帧
-
- [2.1 字符计数法(Character Count)](#2.1 字符计数法(Character Count))
- [2.2 字符填充法(Character Stuffing)](#2.2 字符填充法(Character Stuffing))
- [2.3 零比特填充法](#2.3 零比特填充法)
- [2.4 违规编码法](#2.4 违规编码法)
- 三、差错控制
-
- [3.1 检错编码(奇偶校验码)](#3.1 检错编码(奇偶校验码))
- [3.2 循环冗余校验(Cyclic Redundancy Check, CRC)](#3.2 循环冗余校验(Cyclic Redundancy Check, CRC))
- [3.3 海明校验码(Hamming Code)](#3.3 海明校验码(Hamming Code))
- 四、流量控制与可靠传输
-
- [4.1 滑动窗口机制](#4.1 滑动窗口机制)
- [4.2 停止-等待协议(S-W)](#4.2 停止-等待协议(S-W))
- [4.3 后退N帧协议(GBN)](#4.3 后退N帧协议(GBN))
- [4.4 选择重传协议(SR)](#4.4 选择重传协议(SR))
- [4.5 三种协议的信道利用率分析](#4.5 三种协议的信道利用率分析)
一、数据链路层的功能
数据链路层使用物理层提供的 "比特传输"
服务
数据链路层 为 网络层 提供服务,将网络层的 IP 数据报(分组) 封装成帧,传输给下一个相邻结点
物理链路: 传输介质(0层)+物理层(1层) 实现了相邻结点之间的 "物理链路"
物理链路确实包含两个部分,但更准确的表述是:传输介质(第0层,物理介质层):前面讲到过的电缆(双绞线、同轴电缆)、光纤、无线信道等,负责实际承载信号(电信号、光信号、电磁波);物理层(第1层,协议层):定义如何在介质上传输原始比特流,包括:信号编码(如曼彻斯特编码)、调制解调、时钟同步等。关键区别:传输介质是物理实体(如网线),物理层是协议规范(如以太网 100BASE-TX 标准),两者共同实现
"比特传输"
服务,但属于不同抽象层次。
逻辑链路:数据链路层需要基于 "物理链路"
,实现相邻结点之间逻辑上无差错的 "数据链路(逻辑链路)"
数据链路的功能: 第3章主要围绕数据链路的功能进行讲解(逐个拆解细分)
过程分解(发送端)示例:
python
# 数据链路层构造好一个帧(以字节为单位)
10101010 10101010 10101010 ... (字节序列)
# 物理层将字节拆成比特流
1 0 1 0 1 0 1 0 ... ← bit stream
# 物理层使用编码方案将比特转为信号波形
| 编码方案 | 特点 |
| ----------------------- | ----------------- |
| NRZ(Non-Return-to-Zero) | 高电平=1,低电平=0 |
| 曼彻斯特编码 | 一个比特周期中有电平翻转,用于同步 |
| 4B/5B 编码 + NRZI | 增强同步性和误码检测能力 |
| 8B/10B 编码 | PCIe、USB、光纤通信中常用 |
二、组帧
由于物理层是无结构的比特流传输(bit stream),没有帧的概念,接收方无法知道什么时候是一个数据包的开始和结束。因此需要数据链路层将数据 "打包成帧"
,并定义好帧边界、起止、检测错误等功能。数据链路层通常通过以下几种方式实现帧的起止识别:
2.1 字符计数法(Character Count)
原理: 帧的第一个字段指明整个帧中字符的个数(即帧的总长度),接收方根据这个计数值提取出整个帧
优点: 实现简单,效率较高(无额外的比特填充或转义)
缺点: 如果计数字段本身出错,将导致整个帧的边界错乱(帧同步丢失),后续数据可能无法正确识别
最大缺点: 任何一个计数字段出错,都会导致后续所有帧无法定界
场景: 连续发送三帧(字符计数法)
python
帧1: 长度 4 + 数据(共4字节)00000100 10100001 11110000 00001111
帧2: 长度 3 + 数据(共3字节)00000011 11001100 10101010
帧3: 长度 2 + 数据(共2字节)00000010 11110011
# 帧1长度出错(由4变为6)
00000110 10100001 11110000 00001111 00000011 11001100 10101010 00000010 11110011
# 字节序列(标号)如下:
| 字节号 | 值 | 备注 |
| --- | -------- | ------------------ |
| 1 | 00000110 | 错误的帧1长度(6) |
| 2 | 10100001 | 帧1数据1 |
| 3 | 11110000 | 帧1数据2 |
| 4 | 00001111 | 帧1数据3 |
.....一步错后面的都错位了......
| 5 | 00000011 | 帧1数据4(原为帧2头) ---错位 |
| 6 | 11001100 | 帧1数据5(原为帧2数据) ---错位 |
| 7 | 10101010 | 开始解析下一帧的"长度"字节(错位) |
..........
2.2 字符填充法(Character Stuffing)
原理: 使用特殊字符表示帧的起始和结束,例如 FLAG = DLE STX ... DLE ETX,如果帧数据中出现了这些特殊字符,为避免误解,使用转义字符(如 DLE)进行转义处理
优点: 可以较好处理帧边界识别问题,相比位填充,适用于基于字符的通信系统(如串口通信)
缺点: 只适用于字符流传输(不是比特流),帧内字符一多,转义开销较高
SOH(Start of Header) 和 EOT(End of Transmission) 也确实出现在一些组帧或通信协议中,它们和你前面看到的 STX/ETX 类似,属于字符填充法(即 "基于控制字符的组帧"
),它们是 ASCII 控制字符中的一部分,常用于早期串口通信、串行线路或面向字符的协议中,比如 XMODEM、ZMODEM、早期串行打印通信等:
控制字符 | 十六进制 | 意义 |
---|---|---|
SOH | 0x01 | Start of Header(报头开始) |
STX | 0x02 | Start of Text(正文开始) |
ETX | 0x03 | End of Text(正文结束) |
EOT | 0x04 | End of Transmission(传输结束) |
DLE | 0x10 | Data Link Escape(数据链路转义) |
在字符填充法中,这些控制字符用于标识帧边界或数据部分的结构,例如:
python
# 常见用法一: SOH + 数据 + EOT
# 这种格式有时用于: 一帧一个包,简单可靠,传输完一帧就用 EOT 表明传输完毕
SOH(0x01) 表示帧或报头开始
EOT(0x04) 表示整个传输的结束
# 常见用法二: SOH + 报文头 + STX + 数据 + ETX + EOT
SOH 开始整个帧
STX 开始正文(正文与报头之间分隔)
ETX 正文结束
EOT 整个传输完毕
[SOH][帧头][STX][正文数据][ETX][校验][EOT]
字节填充法图示1:
字节填充法图示2:
字节填充法图示3:
你看到的 "字符填充"
其实可以叫 "字节填充"
,在早期的通信协议中,字符(character)= 字节(byte),因为一个字符就是一个字节(8位),例如 ASCII 编码,所以:"字符填充法"
= "字节填充法"
(在指 8 位字符编码时,是一回事),在使用 ASCII 的协议中,SOH、EOT、ESC 等这些控制字符都是一个字节,所以我们加一个 ESC,就是加了一个字节 ------ 所以叫 "字节填充"
也没错。"字符填充"
突出的是在字符流中插入特殊字符(如 FLAG、ESC、DLE),早期通信设备用的是字符终端(Character-oriented protocols),所以强调 "在字符层级做填充"
。举个例子:
协议类型 | 填充术语常见写法 |
---|---|
HDLC(比特协议) | 比特填充 |
PPP、SLIP(字符协议) | 字符填充 / 字节填充 |
现代协议中为什么偏向说 "字节填充"
?因为现代通信系统中:不再以 "字符"
为核心单位(Unicode 一个字符 ≠ 一个字节),网络传输的单位是字节(byte stream),所以更技术中立、准确的说法是:字节填充(byte stuffing)。
2.3 零比特填充法
原理: 用特定的比特模式来标识一帧的开始和结束,比如 HDLC 使用 01111110。若数据中出现5个连续的 1 比特,发送方自动插入一个 0(填充),接收方在收到5个连续的 1 后发现第6位是 0,就将其删除,从而恢复原始数据。示例:
python
原始数据中包含:01111110
则实际发送为:011111010
接收方解析后将填充的 0 去掉,优点: 适用于位导向协议,可用于任意二进制数据,缺点: 实现稍复杂(需要逐位判断和填充)。
图例1:
图例2:
2.4 违规编码法
原理: 某些物理层编码规则中存在非法的比特模式(正常传输中不会出现),数据链路层使用这些非法模式作为帧的起止标志。示例: 例如在曼彻斯特编码中,若某个比特组合违反电压跳变规则,则认为是帧边界。优点: 不需要增加额外的开销,可在物理层检测帧边界,效率高。缺点: 依赖于物理层的具体实现,不具备通用性。
知识回顾与重要考点:
三、差错控制
数据链路层的差错控制功能
3.1 检错编码(奇偶校验码)
奇偶校验(Parity Check)是一种简单的检错编码技术,用于在数据链路层中检测传输过程中发生的1位错误(bit error)。它的基本思想是:在原始数据后添加一个额外的比特(称为奇偶校验位),使得整个数据的1的个数符合 "奇数"
或 "偶数"
的要求。奇偶校验的两种类型:
- 奇校验码: 整个校验码(有效信息位和校验位)中
"1"
的个数为奇数 - 偶校验码: 整个校验码(有效信息位和校验位)中
"1"
的个数为偶数
【例】给出两个编码 1001101 和 1010111 的奇校验码和偶校验码。设最高位为校验位,余7位是信息位,则对应的奇偶校验码为:
校验码是否一定要放在数据末尾?能不能放在最前面?可以放最前面,也可以放最后,甚至中间都可以,但通常放在末尾(例子放在开头)。这是协议约定问题,不是技术限制。校验位的位置没有本质上的 "必须"
,关键在于发送方和接收方达成一致。但通常放在末尾的原因是:① 数据生成后,再计算校验位比较自然(逻辑清晰)② 接收方收到数据后,只要读完整体,再判断是否符合校验规则 ③ 与 CRC、FCS 等其他编码方式习惯保持一致。
奇偶校验码的生成与判断可以通过非常简单的数字电路(逻辑门)实现,尤其是:偶校验 = 所有比特的异或和,即:
python
data[0] ⊕ data[1] ⊕ data[2] ⊕ ... ⊕ data[n-1]
# 结果是:
# 0 → 偶数个1(满足偶校验)
# 1 → 奇数个1(不满足偶校验)
# ⊕: 异或(模2加)
0 ⊕ 0 = 0
0 ⊕ 1 = 1
1 ⊕ 0 = 1
1 ⊕ 1 = 0 # 两比特相"异" 时,结果为1
# 偶校验: 01001101 11010111
# 求偶校验位:
# 1001101 ==》 经过计算得0,所以前面写0 ==》01001101
1 ⊕ 0 ⊕ 0 ⊕ 1 ⊕ 1 ⊕ 0 ⊕ 1 = 0
# 1010111 ==》 经过计算得1,所以前面写1 ==》11010111
1 ⊕ 0 ⊕ 1 ⊕ 0 ⊕ 1 ⊕ 1 ⊕ 1 = 1
# 进行偶校验(所有位进行异或,若结果为1说明出错):
0 ⊕ 1 ⊕ 0 ⊕ 0 ⊕ 1 ⊕ 1 ⊕ 0 ⊕ 1 = 0 # 正确
# 假设最后一位由1变成了0,则计算结果为1,可以发现出错
1 ⊕ 1 ⊕ 0 ⊕ 1 ⊕ 0 ⊕ 1 ⊕ 1 ⊕ 0 = 1 # 出错
# 可以发现最后两位都由1变成了0,但是计算结果仍然是0,所以:
# 无法检测出偶数位错误
1 ⊕ 1 ⊕ 0 ⊕ 1 ⊕ 0 ⊕ 1 ⊕ 0 ⊕ 0 = 0
奇偶校验的作用和局限性:
-
优点: 实现简单,计算开销非常小(硬件中1个异或门就能实现),可以检测单个比特错误
-
缺点:
缺陷点 说明 检测能力弱 只能检测奇数个错误 (如 1、3、5 位错),偶数位错误无法检测 无法纠错 它只能 "发现"
错误,不能"修正"
错误的位置或内容不能应对高误码环境 在噪声严重的物理环境中效果较差
奇偶校验码常用于以下低速或对可靠性要求不高的场景:
- 老式串口通信(如 RS-232)
- 内存 ECC 简化模式
- 早期网络协议(如 HDLC、PPP)中可作为第一道错误检测
知识回顾:
3.2 循环冗余校验(Cyclic Redundancy Check, CRC)
本小节总览:
CRC 的基本思想是: 把整个数据当成一个 "二进制多项式"
,然后用另一个 "预先约定好的多项式"
去做除法,取余数作为 "校验码"
加在数据末尾,接收方再除一次,如果余数不为0,就说明出错了。这就像你寄快递时在包裹外面贴了个验收码,收件人用规则检查发现对不上,就知道途中被调包/损坏了。
要通俗理解 CRC,需要掌握几个核心概念:
-
数据看成多项式, 假设你要发送的数据是这样一个 8 位二进制串:10110011,我们把它视为一个
"多项式"
,例如:python1·x⁷ + 0·x⁶ + 1·x⁵ + 1·x⁴ + 0·x³ + 0·x² + 1·x¹ + 1·x⁰ # 即为: x⁷ + x⁵ + x⁴ + x¹ + x⁰
这个就叫 数据多项式
-
生成多项式(G(x))是事先约定好的
"除数"
,发送方和接收方都要提前约定一个"生成多项式"
,例如:pythonG(x) = x³ + x + 1 ⇒ 对应二进制是: 1011 # 1 * x³ + 0·x² + 1·x¹ + 1·x⁰
这个多项式的位数 = 校验码的位数 + 1,所以这个 G(x) 的校验码长度是:3位
具体的 CRC 编码过程:
python
# ➤ 第一步: 数据后补 0
# 既然你打算生成一个3位校验码,就在数据后面补上3个0
# 原数据: 10110011
# 补零后: 10110011000 ← 11位
# ➤ 第二步: 用 G(x) 做二进制除法
# 对 10110011000 用生成多项式 1011 做二进制除法(注意这里的除法不是普通的除法,而是用 异或(XOR) 来代替减法):
# 只要会 "竖式除法" 和 "异或运算" 就能算 CRC
# 核心规则是:
# 每次从高位开始,如果被除数的首位是1,就把 G(x) 对齐后异或,否则跳过这一位
# 最终余数就是 CRC 校验码
# 举个直觉化的比喻:
# 它就像你用尺子划过一段木板,哪块凸出来(1)就削一下(异或),平了就跳过
# 最后 "削剩下的" 就是校验码
# ➤ 第三步: 把"余数"加在原始数据后面,假设你除完之后的余数是 101
# 那么最终发送的数据就是:
# 10110011 101
这就是完整的 "数据帧 + CRC校验码"
# 接收方做什么?
# 收到后,接收方也用同样的生成多项式 1011 去除整段 10110011101
# 如果结果 没有余数(即余数为 0) → 数据无误
# 否则说明数据在传输中出错了
这样可能不太好理解,我们结合图示来理解每一个步骤:设生成多项式为 G(x)=x3+x2+1,信息码为 101001,求对应的 CRC 码:
-
确定 K、R 以及生成多项式对应的二进制码。K = 信息码的长度 = 6,多项式对应二进制码为:1011,4 位,减去 1,则 R = 3(也可以直接取生成多项式的最高次幂 3),总位数:N = K + R = 9
-
信息码为 101001,且 R = 3,则补 0 后的数据为:101001000
-
对补 0 后的数据用生成多项式进行模 2 除法,产生余数
-
则对应的 CRC 码 为:001,完整发送的数据:101001001
-
检错和纠错:
总结口诀: CRC 除法就像长除法,只是每次减法用的是异或!CRC 码有些表示的是单独的校验位,不包括原始数据,有些又会把原始数据算上,这个只要你知道在说什么就好了,具体情况具体分析。----- 本小节我也混用了,不管了
拓展: 实际中,CRC 一般不用于纠错,而是用于 错误检测 ,所以下面的了解一下即可。
3.3 海明校验码(Hamming Code)
Richard Hamming 演讲选段:早年,我在攻克一个又一个难题,成功的多,失败的少。可是,周五解决了一个问题回到家里后,我却并不快活,反而很沮丧。我看到生活就是一个问题接着一个问题又接着另一个问题。想了相当长一阵子后,我决定以另一种方式干活:你的工作要成为别人工作的基石!于是别人就会说: "看哪,我站在他的肩膀之上,我看得更远了"
。科学的本质是积累!我再也不去做相互孤立的问题,除非它能代表某一类问题的共性。我决不再去解决单一的问题。你要么让人们在你的成果上有所建树,要么别人不得不把你干的活从头再来复制一遍。
当你通过网络/通信线路发送数据时,可能某一位会出错。普通奇偶校验只能检测是否有错误,但不能定位是哪一位错了,更不能修复它。海明码的目标是: 能发现 1 位错误,能准确定位是哪一位错,并自动纠正。一句话理解: 海明码是一种能检测并纠正1位错误的纠错码,通过插入特定的校验位,使得每一位错误都能被唯一定位。海明码的基本原理: 插入若干校验位,使得每一位都有唯一 "被谁检查"
的组合,把数据分布在一个比原始数据长的 "码字"
中,每个校验位负责检查若干位(包括它自己或不包括)。
需要多少校验位?
海明码求解步骤:
① 确定校验位数量:
② 确定校验位的分布:
③ 求校验位的值:
④ 检查纠错:
⑤ 最后结果:
海明码求解步骤-格式变化:
拓展:
知识回顾:
四、流量控制与可靠传输
4.1 滑动窗口机制
滑动窗口机制是一种用于 提高链路利用率、实现流量控制 和 保证可靠传输 的方法,广泛应用于 数据链路层(如 HDLC 协议) 和 传输层(如 TCP)
发送窗口(Send Window) :发送窗口是发送方维护的一个连续的帧编号区间,它表示当前允许发送但尚未被确认的数据帧范围。窗口大小(N): 可以同时发送的最大未确认帧数。起点: 最早发送但未确认的帧。终点: 起点 + N - 1。如果窗口满了(即未确认的帧数量等于窗口大小),则发送方暂停发送,直到收到确认(ACK)后窗口向前滑动。
接收窗口(Receive Window): 接收窗口是接收方维护的一个帧编号区间,表示其当前准备好接收的数据帧范围,一般与发送窗口同样大小(但不一定),接收到正确的帧后会向前滑动,并发送确认。
重点关注四个方面:
4.2 停止-等待协议(S-W)
要点总览:
停止-等待协议:
数据帧、确认帧、帧序号的概念:
正常情况示例:
异常情况示例:数据帧丢失
异常情况示例:确认帧丢失
为什么一定要给帧编号?
-
区分重复帧和新帧: 由于网络可能出现丢包或延迟,发送方可能因为没收到确认而重发上一帧。接收方通过帧编号可以判断收到的帧是新的还是之前已经接收过的重复帧。如果接收方收到的帧编号和上一次接收的帧编号相同,就说明这是重发的重复帧,应丢弃。如果帧编号不同,说明是新帧,可以接收并发送确认
-
保证数据的顺序和完整性: 帧编号帮助接收方确保数据按正确顺序接收,不会错乱或重复
-
防止确认丢失带来的误解: 如果确认帧(ACK)丢失,发送方会重发上一帧。没有帧编号,接收方无法判断这是重复帧还是新帧,会导致数据混乱。帧编号解决了这个问题
简单举例(文字):
python发送方发送帧0,等待确认 接收方收到帧0,处理并回复ACK0 发送方没收到ACK0,重发帧0 接收方收到帧0,但它已经处理过了,通过编号知道这是重复帧,丢弃并重新发送ACK0 发送方收到ACK0后,发送帧1
异常情况示例:数据帧有差错
知识回顾:
4.3 后退N帧协议(GBN)
要点总览:
后退N帧协议(Go-Back-N Protocol,简称 GBN): 是数据链路层的一种重要滑动窗口协议,用于实现可靠数据传输。后退N帧协议是一种基于滑动窗口的 ARQ(自动重传请求) 协议,它允许发送方在收到确认前连续发送多个帧,从而提高信道利用率。核心特点:
-
发送窗口大于1: 可以连续发送多个帧而无需等待确认
-
累积确认: 接收方只需对按序到达的最后一个正确帧进行确认
-
出错时后退N帧: 当某个帧出错时,发送方需要重传该帧及其后所有已发送但未确认的帧
正常情况示例:
异常情况示例:数据帧丢失
异常情况示例:确认帧丢失
探讨:如果不满足 WT + WR ≤ 2n 会有什么问题?
知识回顾:
4.4 选择重传协议(SR)
要点总览:
选择重传协议(Selective Repeat Protocol,简称 SR) 是数据链路层另一种重要的滑动窗口协议,它通过选择性重传提高了信道利用率。选择重传协议是一种改进的 ARQ 协议,它只重传真正丢失或损坏的帧,而不是像 GBN 那样重传所有未确认帧。核心特点:
- 发送窗口和接收窗口都大于1: 可以缓存多个帧
- 独立确认: 每个正确接收的帧都会被单独确认
- 选择性重传: 只重传真正丢失或损坏的帧
- 乱序接收: 可以接收并缓存乱序但正确的帧
选择重传协议的窗口大小限制条件:
正常情况示例:
异常情况示例:数据帧丢失
异常情况示例:数据帧因差错而被丢失
异常情况示例:确认帧丢失
探讨:如果不满足 WT + WR ≤ 2n 会有什么问题?
知识回顾与重要考点:
4.5 三种协议的信道利用率分析
知识总览:
S-W 协议的信道利用率:
例题:2018真题_36
GBN、SR 协议的信道利用率:
例题:2014真题_36
例题:2015真题_35
术语补充:滑动窗口协议
术语补充:ARQ 协议、连续 ARQ 协议
知识回顾
本文为个人学习记录与复习整理之用,旨在帮助自己系统巩固计算机网络相关知识,同时也希望能为正在学习该领域的同学提供一些参考与帮助。部分内容参考了公开课资料、他人学习笔记或网络公开资源,其中部分图片或示意图来自网络,仅用于非商业性质的学习交流。如有侵权或不当引用之处,敬请联系我删除或更正。
好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。
如果我的博客对你有帮助、如果你喜欢我的博客内容,请
点赞
、评论
、收藏
一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。编码不易,大家的支持就是我坚持下去的动力。点赞后不要忘了
关注
我哦!