16位反码求和、奇偶校验和CRC
本文介绍了网络协议中常见的校验机制,包括IPv4、TCP、UDP、ICMP使用的16位反码求和算法,以及链路层的CRC校验。16位反码求和通过分块、求和、回卷进位和取反操作生成校验和,接收方通过验证全1结果判断数据完整性。CRC则采用模2运算(XOR)进行更严格的校验,由硬件高效完成。两种校验方式各有侧重:反码求和简单高效,CRC检错能力更强,但都不能防止数据篡改。文章通过具体计算示例详细说明了两
校验和——IPv4、TCP、UDP、ICMP&&CRC&&奇偶检验(纠错)
校验和
| 协议 | 校验范围 | 谁负责计算? | 备注 |
|---|---|---|---|
| IPv4 | 仅 Header | 发送方 + 所有路由器 | 因为 TTL 变了,路由器必须更新校验和 (Increment Update)。 |
| IPv6 | 无 | 无 | 为了提速,移除了校验和,依赖 TCP/UDP 和 链路层校验。 |
| UDP | Header + Data + 伪首部 | 仅终端 (端到端) | 可选的(但在 IPv6 中是强制的)。 |
| TCP | Header + Data + 伪首部 | 仅终端 (端到端) | 强制的,保证数据可靠性。 |
| Ethernet | 整个帧 | 网卡硬件 | 使用 CRC-32 算法,硬件实现,速度极快。 |
使用16位反码求和算法的IPv4、TCP、UDP、ICMP
都使用同一种算法,称为 “16位反码求和” (16-bit One’s Complement Sum)
计算步骤(发送方):
- 分块: 把需要校验的内容(Header 或 Header+Data)看成是由许多个 16位(2字节) 的整数组成的序列
- 补零: 如果数据字节数是奇数,最后补一个字节的 0,凑成偶数**(16位对齐)→一般不会出现发送单个bit的情况**
- 求和: 把这些 16 位的整数进行二进制相加
- 回卷(Wrap Around): 如果加法产生了最高位的进位(溢出,超过 16 位),把进位加回到结果的最低位(这就是“反码加法”的特性)
- 取反: 最后把求和的结果按位取反(0变1,1变0)
- 填入: 将这个结果放入 Checksum 字段中
验证步骤(接收方):
- 接收方收到数据后,把所有数据(包括 Header、Data 和 接收到的 Checksum 字段本身)按同样的规则切分成 16 位整数
- 将它们全部相加(同样包含进位回卷)
- 结果判断:
- 如果结果是 全 1 (1111 1111 1111 1111,即 -0),说明传输正确
- 否则,说明传输出错,丢弃该包
例子:对于两个数11100111 00010001和01101110 00111011来说
- 字1 (Word 1): 11100111 00010001
- 字2 (Word 2): 01101110 00111011
接收方操作:
第一步:把收到的 数据 (字1 + 字2) 和 校验和 全部加起来
字1 + 字2 = 1 01010101 01001100 -> 回卷后 -> 01010101 01001101
01010101 01001101
+ 10101010 10110010
-------------------
11111111 11111111
第二步:判断正确与否——全 1 代表校验通过,数据未损坏
发送方操作:
第一步:二进制相加 (求和)
11100111 00010001 (字1)
+ 01101110 00111011 (字2)
-------------------
**注意:这里产生了进位** (Overflow)->1 01010101 01001100
第二步:回卷 (Wrap Around)
01010101 01001100 (去掉溢出位后的结果)
+ 1 (把溢出的 1 加回来)
-------------------
01010101 01001101 <-- 这是“回卷”后的和
第三步:取反 (One’s Complement) —— 得到校验和
01010101 01001101
------------------
10101010 10110010
链路层的 循环冗余校验 (Cyclic Redundancy Check)
链路层(比如 Ethernet 以太网、Wi-Fi)通常使用 CRC,而不是简单的加法校验,比刚才讨论的“IP/TCP 校验和(反码求和)”更强大、更严格
通常由网卡硬件直接完成,不占用 CPU。检错能力极强,是数据链路层的保镖
CRC 的除法和我们需要借位的普通除法不同,它使用的是 模2运算 (Modulo-2 Arithmetic)。
简单说就是:不进位,也不借位,其实就是 XOR (异或) 运算
- 0 XOR 0 = 0
- 1 XOR 1 = 0
- 1 XOR 0 = 1
- 0 XOR 1 = 1
- 约定规则: 发送方和接收方提前商定一个除数(称为生成多项式,Generator Polynomial)
- 发送方: 拿着要发送的数据,在后面补上几个 0,然后用这个数据去除以那个约定的除数。除完之后,会得到一个余数
- 发送方把这个余数贴在数据的尾巴上发出去
- 接收方: 收到数据(含尾巴)后,用同样的除数去除这个数据
- 如果余数是 0,说明数据是完美的
- 如果余数不为 0,说明数据在路上坏了,直接丢弃
- 要发送的数据 (D): 101001
- 约定的除数 (G): 1101 (在多项式中表示 x3+x2+1x^3 + x^2 + 1x3+x2+1)
- 注意:除数是 4 位,所以我们要在数据后面补 3个零 (即发送数据的位数-1)
步骤一:补零
数据变成:101001 + 000 = 101001000
步骤二:进行模2除法 (XOR 除法)
1 1 0 1 0 1 <-- 商 (我们不关心商)
_________________
除数 1101 ) 1 0 1 0 0 1 0 0 0 <-- 被除数 (数据+补零)
1 1 0 1 <-- 1. 做 XOR 运算 (1-1=0, 0-1=1, 1-0=1, 0-1=1)
-------
1 1 1 0 <-- 余下部分,拉下一位
1 1 0 1 <-- 2. 做 XOR
-------
0 1 1 1 <-- 余下部分,拉下一位
0 0 0 0 <-- (首位是0,商0,下面全写0)
-------
1 1 1 0 <-- 余下部分,拉下一位
1 1 0 1 <-- 3. 做 XOR
-------
0 1 1 0 <-- 余下部分,拉下一位
0 0 0 0 <-- (首位是0)
-------
1 1 0 0 <-- 余下部分,拉下一位
1 1 0 1 <-- 4. 做 XOR
-------
0 0 1 <-- 余数 (Remainder)
余数是 001。这个余数就是 CRC 校验码(也叫 FCS,帧校验序列)
步骤三:发送数据
发送方实际发送的数据是:原始数据 + **余数,**即:101001 + 001 = 101001001
接收方收到了 101001001,它用同样的除数 1101 再做一次除法
1 1 0 1 0 1 <-- 商 (这一行我们其实不关心)
___________________
除数 1 1 0 1 ) 1 0 1 0 0 1 0 0 1 <-- 被除数 (收到的完整数据)
1 1 0 1 <-- 【第1步】首位是1,用 1101 做异或
-------
1 1 1 0 <-- 结果 (1010^1101=0111),拉下下一位 0
1 1 0 1 <-- 【第2步】首位是1,用 1101 做异或
-------
0 1 1 1 <-- 结果 (1110^1101=0011),拉下下一位 1
0 0 0 0 <-- 【第3步】首位是0,不够除,用 0000 做异或
-------
1 1 1 0 <-- 结果 (0111^0000=0111),拉下下一位 0
1 1 0 1 <-- 【第4步】首位是1,用 1101 做异或
-------
0 1 1 0 <-- 结果 (1110^1101=0011),拉下下一位 0
0 0 0 0 <-- 【第5步】首位是0,不够除,用 0000 做异或
-------
1 1 0 1 <-- 结果 (0110^0000=0110),拉下下一位 1 (最后一位)
1 1 0 1 <-- 【第6步】首位是1,刚好用 1101 做异或
-------
0 0 0 <-- 【最终结果】余数为 0
计算出的最终余数是 **0,**这意味着:
- 接收方确认发送方用的除数也是 1101
- 传输过程中,没有任何一个比特发生翻转(数据完好无损)
CRC 不能用于验证数据的完整性(防篡改)。如果需要防篡改,必须使用 Hash 算法(如 SHA-256) 或 数字签名。
CRC使用软件实现起来很慢:
- 硬件实现(快):在网卡、硬盘控制器里,CRC 是通过移位寄存器和异或门电路实现的,速度极快,几乎没有延迟。
- 软件实现(慢):如果让 CPU 用代码去跑 CRC 的除法运算,效率是比较低的(涉及大量的位移和异或操作)。
- 对比:TCP/IP 协议栈之所以在网络层和传输层使用简单的 Checksum(反码求和) 而不用 CRC,就是为了减轻 CPU 的负担(虽然 Checksum 检错能力不如 CRC,但 CPU 算加法非常快)。
CRC 的效果好不好,完全取决于那个生成多项式(Generator Polynomial)选得好不好
- 工业界规定了几个特定的“黄金多项式”,比如:
- CRC-32 (以太网、ZIP压缩包使用)
- CRC-16-CCITT (蓝牙、SD卡使用)
- 问题:如果你在设计协议时没有使用标准多项式,而是自己瞎编了一个,很可能导致某些特定的错误模式(比如连续翻转2位)无法被检测出来。
奇偶校验
奇偶校验的核心思想是**“凑数”:**
- 偶校验(Even Parity):添加一个校验位,使得整个数据(包括校验位)中 1 的个数是偶数。
- 奇校验(Odd Parity):添加一个校验位,使得整个数据(包括校验位)中 1 的个数是奇数。
首先,它们在检错能力上是完全一样的,它们都只能检测出奇数个比特翻转(比如变了1位、3位),如果同时变了2位,它们都会失效。
这个奇偶检验说的是1的个数是奇数还是偶数,不是说数据长度是奇数还是偶数!!!
奇校验(Odd Parity) 有一个偶校验无法比拟的优势,那就是检测“全0”故障
假设你有一根传输线,因为硬件故障(断路、没电、接口松动),这根线被拉低电平,导致接收端收到的全是 0。如果用偶校验:数据是 00000000,校验位也是 0(因为0个1被认为是偶数)
接收端误以为收到了一段有效的“空数据”→后果:无法发现线路已经断了
如果用奇校验:正常情况下,如果数据是 00000000,校验位必须是 1(凑成1个1),现在线路坏了,收到的是全0。→接收端收到一串0,数了一下1的个数是0(非奇数),校验失败!→接收端立刻知道出问题了,要么是数据错了,要么是线路断了
在网络层(如TCP/IP、以太网)主要用更高级的 CRC(循环冗余校验),但在底层硬件中,奇偶校验依然存在
单比特奇偶校验 (Single Bit Parity)
- 原理:发送方在数据末尾加 1位 校验位
- 如果原始数据里有奇数个1→校验位填 1(凑成偶数)
- 如果原始数据里有偶数个1→校验位填 0(保持偶数)
假设我们要发送数据 10110(这里有3个1)→3个1,有奇数个1→校验位填1
- 发送端:为了凑成偶数个1,校验位设为 1。发送的数据变为 10110 + 1 = 101101(共4个1)
- 接收端:收到数据后数一下1的个数
- 情况A:收到 101101 →4个1(偶数)认为没问题。
- 情况B(出错):收到 100101 →3个1(奇数)检测出错误!
- 缺点:
- 只能检测奇数个比特错误:如果同时坏了2个比特(比如 101101 变成了 000101),1的个数从4个变成了2个,依然是偶数。接收方会误以为数据是正确的。
- 无法纠错:接收方只知道“出错了”,但不知道是哪一位错了(是第1位还是第5位?),所以只能要求重传。
二维奇偶校验 (Two-Dimensional Bit Parity)
为了解决单比特校验“太弱”的问题,二维校验引入了矩阵的概念。它不仅能检测错误,还能纠正错误
- 原理:将要发送的数据切分成若干段,排成一个矩阵(行和列)。
- 行校验:对每一行计算一个校验位
- 列校验:对每一列计算一个校验位
- 最后还会有一个右下角的总校验位
如果矩阵中某一个数据位发生了翻转(0变1):
- 它所在的行,1的个数会变,导致行校验失败
- 它所在的列,1的个数也会变,导致列校验失败
- 行错误和列错误的交汇点,就是出错的那个比特!→这就是为什么可以进行纠错的原因(定位到了错误的bit就可以直接进行翻转纠错了)
**A. 发送端(计算校验位):**假设数据是 10110 和 01110
| 数据位 d1 | 数据位 d2 | 数据位 d3 | 数据位 d4 | 数据位 d5 | 行校验位 | |
|---|---|---|---|---|---|---|
| 第一行 | 1 | 0 | 1 | 1 | 0 | 1 (凑4个1) |
| 第二行 | 0 | 1 | 1 | 1 | 0 | 1 (凑4个1) |
| 列校验位 | 1 | 1 | 0 | 0 | 0 | 0 |
B. 传输过程中出错:假设第一行、第二列的那个 0 变成了 1
| 数据位 d1 | 数据位 d2 | 数据位 d3 | 数据位 d4 | 数据位 d5 | 行校验位 | ||
|---|---|---|---|---|---|---|---|
| 出错行 | 1 | 1(错) | 1 | 1 | 0 | 1 | 有5个1,不是偶数→出错 |
| 第二行 | 0 | 1 | 1 | 1 | 0 | 1 | |
| 列校验 | 1 | 1 | 0 | 0 | 0 | 0 | |
| 有3个1不是偶数→出错 |
C. 接收端(检测与纠正):
- 检查行:第一行现在有5个1(奇数),报错!
- 检查列:第二列现在有3个1(奇数),报错!
- 定位:错误出现在 第一行 和 第二列 的交叉点。
- 纠正:既然该位是 1 且它是错的,那正确的值一定是 0(因为二进制只有0和1)。直接反转该位,无需重传。
- 优点:可以检测并纠正单个比特错误;可以检测**(但不能纠正)**两个比特的错误。
- 缺点:开销比单比特校验大(需要传输更多的校验位)。如果错误太多(例如一个矩形区域的4个角都错了),它也无能为力。
更多推荐



所有评论(0)