ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

计算机网络链路层核心考点:HDLC、PPP、CRC与Go-back-N详解

计算机网络链路层核心考点:HDLC、PPP、CRC与Go-back-N详解 简介这份PPT面向正在学习计算机网络原理的高校学生与备考人员聚焦数据链路层这一核心章节帮助读者通过历年真题巩固HDLC、PPP、CRC校验、流量控制与差错控制编码等高频考点。资源包内含1个PPT文件整体约1.32MB以幻灯片形式集中呈现试题与参考答案便于课堂复习或考前突击。内容按考试年份编排收录2008年至2009年多次考试的选择题、填空题与计算题并附有详细解答过程例如CRC生成多项式校验、调制解调器传输时间计算、停等协议与滑动窗口机制分析、HDLC帧格式绘制等典型题型。已有37人学习适合需要系统梳理第四章知识点、熟悉命题风格并查漏补缺的读者使用。1. 从一份“计算机网络原理第四章试题汇总.ppt”说起链路层到底在考什么如果你手头正好有一份“计算机网络原理第四章试题汇总.ppt”大概率是数据链路层那一章。翻开来反复出现的无非这几个词HDLC、PPP、CRC、Go-back-N。很多人复习时把它们当四块孤立的知识点背结果一到算 CRC 校验码、数滑动窗口序号就翻车。其实这一章的主线很清楚链路层要解决的是“相邻两个节点之间怎么把一串比特可靠地交过去”。HDLC 和 PPP 是两种成帧协议CRC 是差错检测手段Go-back-N 是出错之后的重传策略。把这四样串成一条线试题里八成以上的计算和简答都能顺下来。这篇笔记就按这条线走先讲清每个点为什么这么设计再落到能动手复现的步骤和参数最后把常见的坑摆出来。适合正在啃这一章的学生也适合需要快速把链路层知识捡回来的从业者。2. HDLC 与 PPP 的成帧差异从比特填充到字节填充2.1 为什么链路层一定要“成帧”物理层送上来的是连续比特流接收方根本不知道哪一段是一个完整的数据单元。成帧就是给数据加上明确的起止标记让接收方能从比特流里切出一个个帧。HDLC 和 PPP 都干这件事但标记方式和适用场景不同这也是试题里最爱对比的地方。HDLC 面向比特用标志字段01111110作为帧的起止。问题是数据里如果恰好出现连续的 1就可能和标志混淆。解决办法是比特填充发送方在数据中每遇到 5 个连续 1就插入一个 0接收方收到 5 个连续 1 后跟一个 0就把这个 0 删掉。这样数据里永远不会出现 6 个连续 1标志字段就唯一了。PPP 面向字节常用0x7E作为帧定界符。如果数据里出现0x7E或转义字符0x7D就用字节填充把该字节异或0x20后再前面加一个0x7D。比如0x7E变成0x7D 0x5E0x7D变成0x7D 0x5D。PPP 还支持异步和同步两种链路异步用字节填充同步用比特填充这一点试题里经常拿来设陷阱。2.2 用一段 Python 复现比特填充与字节填充光看规则容易记混直接写几行代码跑一遍印象会深很多。下面这段脚本同时实现 HDLC 的比特填充和 PPP 的字节填充你可以拿它验证试题里的填充结果。def bit_stuff(data_bits): HDLC 比特填充每 5 个连续 1 后插入一个 0 result [] count 0 for bit in data_bits: result.append(bit) if bit 1: count 1 if count 5: result.append(0) # 插入 0 count 0 else: count 0 return .join(result) def byte_stuff(data_bytes): PPP 字节填充转义 0x7E 和 0x7D result bytearray() for b in data_bytes: if b 0x7E: result.extend([0x7D, 0x5E]) # 0x7E ^ 0x20 0x5E elif b 0x7D: result.extend([0x7D, 0x5D]) # 0x7D ^ 0x20 0x5D else: result.append(b) return bytes(result) # 测试 print(bit_stuff(11111011111)) # 观察插入 0 的位置 print(byte_stuff(b\x7e\x7d\x01).hex()) # 观察转义结果逻辑说明bit_stuff维护一个连续 1 的计数器达到 5 就补 0 并清零保证数据段不会出现 6 个连续 1。byte_stuff只对0x7E和0x7D两个特殊字节做异或0x20并加转义前缀其他字节原样输出。参数上比特填充的阈值固定是 5这是 HDLC 标准规定的字节填充的转义字符是0x7D异或值是0x20这两个值在 PPP 的 RFC 里有明确定义试题里如果改了这两个值那一定是错的。2.3 HDLC 和 PPP 的帧结构对比试题里常考帧格式字段的含义下面这张表把两者放在一起方便对照记忆。字段HDLCPPP标志011111100x7E地址8 位通常全 10xFF广播控制8 或 16 位含序号0x03无序号帧协议无独立协议字段2 字节标识上层协议信息可变长可变长默认最大 1500 字节FCSCRC-16 或 CRC-32CRC-16填充方式比特填充字节填充异步从表里能看出PPP 比 HDLC 多了协议字段这是为了支持多种网络层协议复用同一条链路。HDLC 的控制字段承载了序号和确认所以它本身就能做可靠传输PPP 默认不提供可靠传输靠上层保证。这个差异直接决定了后面 Go-back-N 和 CRC 在两种协议里的角色不同。3. CRC 校验码计算从生成多项式到余数3.1 CRC 的数学直觉为什么模 2 除法能检错CRC 的核心是把数据看成多项式用一个约定的生成多项式去除余数就是校验码。发送方把余数附在数据后面接收方用同一个生成多项式去除整个码字余数为 0 就认为没出错。这里的除法是模 2 除法加减都是异或不产生进位借位所以硬件实现特别简单一个移位寄存器加几个异或门就能跑。生成多项式的最高位和最低位必须是 1这是为了保证检错能力。热搜里那句“不能作为 CRC 生成多项式”说的就是这种情况如果最低位是 0那么任何奇数个比特错误都可能检不出来因为余数永远是偶数。试题里经常给几个多项式让你判断哪个不能选记住“首尾必须为 1”就能秒杀。3.2 手算 CRC 校验码的完整步骤假设生成多项式是G(x) x^4 x 1对应二进制10011数据是110101。步骤如下在数据后面补 4 个 0因为生成多项式最高次是 4。得到1101010000。用模 2 除法除以10011每次对齐最高位做异或。除到最后剩下的 4 位余数就是 CRC 校验码。把余数附在原始数据后面形成发送码字。下面用 Python 把这个过程写出来你可以直接改数据和多项式验证。def crc_remainder(data_bits, poly_bits): 模 2 除法求余数data_bits 已补零 data list(data_bits) poly list(poly_bits) poly_len len(poly) for i in range(len(data) - poly_len 1): if data[i] 1: for j in range(poly_len): # 异或操作模 2 加法 data[i j] str(int(data[i j]) ^ int(poly[j])) return .join(data[-(poly_len - 1):]) def crc_encode(data_bits, poly_bits): 生成 CRC 校验码并拼接 appended data_bits 0 * (len(poly_bits) - 1) remainder crc_remainder(appended, poly_bits) return data_bits remainder # 测试数据 110101生成多项式 10011 print(crc_encode(110101, 10011))逻辑说明crc_remainder从高位到低位扫描只要当前位是 1 就和生成多项式对齐异或模拟手算的模 2 除法。crc_encode先补零再求余最后把余数拼回数据后面。参数上生成多项式的位数决定了补零个数和余数长度比如10011是 5 位补 4 个零余数 4 位。试题里如果给的是x^16 x^15 x^2 1那就是 CRC-16补 16 个零余数 16 位。3.3 接收端怎么验证接收端收到码字后用同一个生成多项式去除整个码字。如果余数为 0就认为传输无误余数不为 0说明出错了。注意 CRC 只能检错不能纠错纠错要靠重传。这也是为什么 CRC 总是和 Go-back-N 这类重传机制一起出现。4. Go-back-N 的窗口与重传序号到底怎么数4.1 滑动窗口的基本规则Go-back-N 是滑动窗口协议的一种。发送方可以连续发送多个帧而不等确认但窗口大小有限。接收方只按序接收如果收到失序的帧就直接丢弃并重复确认最后一个按序收到的帧。发送方一旦超时就从那个未被确认的帧开始把所有后续帧全部重传。这就是“回退 N”的含义。窗口大小和序号位数的关系是试题里的高频计算点。如果序号用 n 位表示序号空间是 2^n。Go-back-N 要求发送窗口大小 W ≤ 2^n - 1。为什么因为如果 W 2^n接收方就无法区分“新帧”和“重传帧”会出现二义性。这个结论一定要记牢选择题和计算题都爱考。4.2 用代码模拟一次 Go-back-N 的发送过程下面这段模拟展示了窗口滑动、超时重传和累计确认的过程你可以调整窗口大小和丢包位置观察行为。def go_back_n(total_frames, window_size, lost_frames): 模拟 Go-back-N 发送过程 base 0 # 窗口起始序号 next_seq 0 # 下一个待发序号 acked -1 # 最后一个被确认的帧 steps [] while base total_frames: # 发送窗口内的帧 while next_seq base window_size and next_seq total_frames: if next_seq not in lost_frames: steps.append(f发送帧 {next_seq}) else: steps.append(f帧 {next_seq} 丢失) next_seq 1 # 假设接收方按序确认遇到丢失就停止确认 for seq in range(base, next_seq): if seq in lost_frames: break acked seq if acked base: base acked 1 steps.append(f确认到帧 {acked}窗口滑动到 {base}) else: steps.append(f超时从帧 {base} 开始重传) next_seq base # 回退重传 return steps # 测试共 6 帧窗口 3帧 2 丢失 for s in go_back_n(6, 3, {2}): print(s)逻辑说明base是窗口左边界next_seq是下一个要发的序号。发送时只发窗口内的帧遇到丢失帧就记录。确认阶段模拟接收方按序确认一旦遇到丢失帧就停止所以acked停在丢失帧前一个。如果acked没有前进就触发超时把next_seq拉回base重传。参数上window_size不能超过2^n - 1lost_frames用来模拟链路丢包。你可以把窗口改成 4、序号位数设成 3 位观察什么时候会出现二义性。4.3 累计确认和捎带确认的区别Go-back-N 用的是累计确认接收方确认序号 n表示 n 及之前的所有帧都收到了。这和选择重传不同选择重传会单独确认每个收到的帧。试题里经常把两者混在一起考记住 Go-back-N 的接收窗口是 1只能按序收所以它只能累计确认。捎带确认是双向通信时把确认信息搭在数据帧里发回去和窗口机制是两回事不要混淆。5. 避坑与排查CRC 和滑动窗口里最容易翻车的地方5.1 现象CRC 余数算出来位数不对原因补零个数搞错了。补零个数等于生成多项式的最高次也就是多项式二进制位数减一。比如10011是 5 位最高次是 4补 4 个零。很多人直接补成 5 个零余数就多一位。解决先数生成多项式的位数减一就是补零个数也是余数位数。算完检查余数长度是否等于位数减一。5.2 现象Go-back-N 窗口大小设成 2^n 后接收方分不清新旧帧原因序号空间是 2^n如果窗口大小也是 2^n发送方重传的帧序号会和下一轮新帧序号完全重叠。接收方收到同一个序号无法判断这是重传还是新帧。解决发送窗口最大只能取 2^n - 1。如果试题给的是选择重传接收窗口最大是 2^(n-1)这个边界也要分清。5.3 现象PPP 字节填充时把普通字节也转义了原因转义规则只针对0x7E和0x7D两个字节其他字节一律原样发送。有人误以为所有控制字符都要转义结果把0x03也转了接收方解析就乱了。解决严格按 RFC 定义只转义帧定界符和转义字符本身。异步 PPP 里小于0x20的字符是否转义取决于具体实现但试题通常只考0x7E和0x7D。5.4 现象HDLC 比特填充后接收方删 0 删多了原因接收方看到 5 个连续 1 就删后面的 0但如果这个 0 本来就是数据里的就会误删。实际上发送方保证数据里不会出现 5 个连续 1 后跟 0 的情况因为它在第 5 个 1 后面插了 0。接收方删 0 的前提是确认这 5 个 1 是数据的一部分而不是标志字段。解决接收方要先识别标志字段01111110在标志之外的区域才做删 0 操作。标志字段本身不参与填充和删除。5.5 现象CRC 校验通过但数据仍然错了原因CRC 不是万能的它只能检测一定范围内的错误。如果错误模式恰好是生成多项式的倍数余数就会是 0校验通过但数据错了。热搜里“无法保证检出全部奇数个比特错误”说的就是生成多项式选得不好时的情况。解决选择标准的生成多项式比如 CRC-16 用x^16 x^15 x^2 1CRC-32 用 IEEE 802.3 规定的多项式。这些多项式经过验证检错能力有保证。不要自己随便造一个。6. 把四个考点串成一条线用一道综合题验证掌握程度6.1 综合题从成帧到重传的完整链路假设一条链路用 HDLC 成帧生成多项式为x^4 x 1发送窗口大小为 3序号用 3 位表示。现在要发送数据110101请回答成帧后的比特流是什么CRC 校验码是多少如果第 2 帧丢失Go-back-N 会重传哪些帧这道题把 HDLC 比特填充、CRC 计算和 Go-back-N 重传全串起来了。你可以先自己算再用前面的代码验证。比特填充时注意标志字段不参与填充CRC 计算时注意补零个数Go-back-N 重传时注意窗口大小和序号空间的关系。6.2 用代码一次性验证三个环节下面这段脚本把三个环节串起来输入数据和参数就能看到完整结果。def full_pipeline(data_bits, poly_bits, window_size, total_frames, lost_frame): # 第一步CRC 编码 encoded crc_encode(data_bits, poly_bits) print(fCRC 编码结果: {encoded}) # 第二步HDLC 比特填充 stuffed bit_stuff(encoded) print(f比特填充后: {stuffed}) # 第三步Go-back-N 重传 steps go_back_n(total_frames, window_size, {lost_frame}) print(Go-back-N 过程:) for s in steps: print( s) # 运行综合示例 full_pipeline(110101, 10011, 3, 6, 2)逻辑说明full_pipeline先做 CRC 编码把余数拼到数据后面然后对比特流做 HDLC 填充最后模拟 Go-back-N 发送过程。参数上data_bits是原始数据poly_bits是生成多项式window_size是发送窗口total_frames是总帧数lost_frame是丢失的帧序号。你可以改这些参数观察每一步输出的变化。6.3 我踩过的坑和现在的习惯早年复习这一章时我总把 CRC 和 Go-back-N 分开背结果一到综合题就卡壳。后来强迫自己每做一道题就把四个考点过一遍先看成帧方式再算 CRC再画滑动窗口最后写重传序列。这个习惯让我在考试里再也没漏过步骤。另外生成多项式我从来不死记而是先写二进制再数位数补零个数和余数长度自然就出来了。窗口大小我习惯先算 2^n再减一这样不会把 Go-back-N 和选择重传搞混。如果你也在啃这一章建议把上面的代码跑一遍改几个参数看看输出怎么变比单纯看书快得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表