787 字
4 分钟
IP、TCP、UDP校验和
二进制反码求和
先对每 16 位求反码然后求和,若相加后最高位有进位,那么不能舍弃,一定要加到低位,才能是结果正确。如0x0319BB 拆分成0X03+0X19BB(先求和再求结果的反码也是可以的,但是进位回卷到低位是要做的)。 不依赖系统是大端小端。即无论你是发送方计算机或者接收方检查校验和时,都不要调用htons或者ntohs,直接通过上面的算法就可以得到正确的结果。这个问题可以自己举个例子,用反码求和时,交换16位数的字节顺序,得到的结果相同,只是字节顺序相应地也交换了;而如果使用原码或者补码求和,得到的结果可能就不同。
以IP校验和为例子

求校验和
- 把校验和字段以全零填充
- 把报文头视作16位整数的数组,报文头不是整数个16位则往后填充0
- 对每 16 位(2 Byte)进行二进制反码求和
- 对得到的结果即为首部校验和。
验证
- 把报文头视作16位整数的数组,报文头不是整数个16位则往后填充0
- 对每 16 位(2 Byte)进行二进制反码求和,结果为全1
const makeChecksum = (data) => {
const res = data.reduce((acc, cur) => acc + cur)
return 0xFFFF - ((res & 0xFFFF) + (res >> 16))
}
const verify = (data) => {
const res = data.reduce((acc, cur) => acc + cur)
return 0xFFFF == (res & 0xFFFF) + (res >> 16)
}
const data = [0x45c0,0x0044,0x0052,0x0000,0x0159,0x0000,0x5900,0x0009,0xe000,0x0005]
makeChecksum(data).toString(16) // 0x7F41
verify([0x45c0,0x0044,0x0052,0x7f41,0x0159,0x0000,0x5900,0x0009,0xe000,0x0005]) //true
UDP
伪首部
由于UDP首部中不包含源地址与目标地址等信息,为了保证UDP校验的有效性,在进行UDP校验和的计算时,需要增加一个UDP伪首部 只有在计算检验和时才出现, 不向下传送也不向上递交
k k伪首部内容类似于IP报文首部,共12B: 源地址(4B),目标地址(4B),0(1B),17(1B 即UDP的协议号),UDP长度(2B UDP首部8B+数据部分长度)
过程
发送方
0. 若不使用校验,则将校验和字段全部置0
- 加上伪首部
- 全0填充检验和字段
- 全0填充数据部分(使整个UDP数据报是4字节的整数倍)
- 将伪首部+首部+数据字段进行二进制反码求和
- 以16bit(2B)为一组分开进行求和(校验和字段长度)
- 将求和结果填入检验和字段
- 若结果恰好全为0,则全部填入1 (回绕)
- 去掉伪首部
- 发送
接收方
- 加上伪首部
- 将伪首部+首部+数据字段进行二进制反码求和
- 结果全为1,则无差错
- 否则视情况丢弃数据,或交给应用层并附上差错警告
TCP校验和
同UDP不过协议号是6
对比
- 校验范围
- IP校验和只校验报头
- TCP和UDP校验整个报文还包括伪首部
- UDP校验是可选的全0表示忽略校验
