一文读懂网络报问中的检验和(checksum)—— 原理+举例+代码

如何求解网络报文中的checksum?

最近在学习计算机网络,在运输层和网络层部分存在各种协议TCP、UDP、IP、ICMP等等,而在这些报文中都存在一个公共的字段——检验和(checksum)。接下来,我将从什么是检验和、检验和怎么算、检验和计算示例、python代码计算检验和这几个部分详细介绍。

什么是检验和?

检验和是存在于各种报文中的一个字段,它存在的目的是验证报文在网络传输过程中的完整性(有的数据可能在链路传输时发生0-1数据翻转,从而导致报文出错)。因此,在报文的发送端,会根据报文中的首部或数据来计算一个检验和(IP报文的检验和只对首部进行计算,ICMP报文对报文首部和数据都进行计算),然后一旦接收端接受到相应报文,接收端也会对报文的首部或数据进行一次检验和计算,如果接收端算出来的检验和和发送端发送的不一样,那么对不起,接收端认为报文在传输过程中出了错,于是就丢掉该报文。

算法归纳

待检验部分从头开始,每16比特进行一次加法计算(如果最后有8位剩余,最后加上这8位),这样最终计算出来的和进行一次反码运算,就是检验和。
注意:如果求和过程中遇到了任何溢出,都进行回卷(即加回到最低位)

example

在这里插入图片描述

IP报文各字段

看完上面还迷迷糊糊的?没关系,在这里,就以IP报文为例,介绍更多的细节。首先,还是先把IP报文的各个字段信息回顾一下,如下图。如果你看到这个,有些字段忘记了它的意思,就百度回忆一下吧,这里我就不多说了。
在这里插入图片描述

IP首部检验和示例

现在假设有一个IP报文的首部如下所示(都是16进制数):
4500 003c 1c46 4000 4006 b1e6 ac10 0a63 ac10 0a0c

ok,我们将这个报文的各个字段一一来匹配一下

  • 45 —— 对应IP首部前8位,4是version字段,表示IPV4,5是首部长度字段,但注意,首部长度是以每4个字节为1个单位的,所以这里就是5*4=20个字节 (这也说明IP报文首部的option字段和padding字段没用上)
  • 00 —— 对应服务类型(TOS,type of service)字段,00表明是正常操作
  • 003C —— 对应total length字段,说明改IP报文首部加数据段一共是60字节(也就是说,数据段部分占了40字节)
  • 1C46 —— 对应报文标识符字段
  • 4000 —— 对应flags和fragment offset字段,其中flag字段占3位,分片偏移占13位
  • 40 —— 对应TTL字段(Time to live),表明该报文可以经过40跳(hops)
  • 06 —— 对应IP报文封装的上层协议代码,这里是6,表明是TCP报文
  • b1e6 —— 这就是发送端即将求出来的检验和
  • ac10 0a63 —— 对应IP报文的源IP地址
  • ac10 0a0c —— 对应IP报文的目的IP地址

发送端

Alright ! 终于可以开始计算了。 : )

说明:在计算检验和字段之前,我们先把检验和的16位全部置0。

我们现在把16进制数全部转为二进制,如下:

4500 -> 0100010100000000
003c -> 0000000000111100
1c46 -> 0001110001000110
4000 -> 0100000000000000
4006 -> 0100000000000110
0000 -> 0000000000000000   // 先全部置零,最后再把算出来的结果附加上
ac10 -> 1010110000010000
0a63 -> 0000101001100011
ac10 -> 1010110000010000
0a0c -> 0000101000001100

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

然后我们每16位进行一次加法运算:

4500 -> 0100010100000000
003c -> 0000000000111100
453C -> 0100010100111100  // 第一次计算结果

453C -> 0100010100111100 // 第一次计算结果 加 后16位
1c46 -> 0001110001000110
6182 -> 0110000110000010 // 第二次计算结果

6182 -> 0110000110000010 // 第二次计算结果 加 后16位
4000 -> 0100000000000000
A182 -> 1010000110000010 // 第三次计算结果

A182 -> 1010000110000010 // 第三次计算结果 加 后16位
4006 -> 0100000000000110
E188 -> 1110000110001000

E188 -> 1110000110001000
AC10 -> 1010110000010000
18D98 -> 11000110110011000 // 这里产生了一次溢出,根据回卷规则,把溢出位加到最后

18D98 -> 11000110110011000
8D99 -> 1000110110011001 // 进行类似16位加法。。。就不再重述了

8D99 -> 1000110110011001
0A63 -> 0000101001100011
97FC -> 1001011111111100

97FC -> 1001011111111100
AC10 -> 1010110000010000
1440C -> 10100010000001100 // 由产生了进位,继续回卷

1440C -> 10100010000001100
440D -> 0100010000001101

440D -> 0100010000001101
0A0C -> 0000101000001100
4E19 -> 0100111000011001

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

Well,终于加完了,别急还没完!!!还要求一次反码,别忘了哈~~

4E19 -> 0100111000011001
B1E6 ->1011000111100110  // 检验和

 
 
  • 1
  • 2

好了,这样我们再把b1e6这个检验和放进到IP报文的checksum字段,检验的工作也就此完成。

接收端

接收端就比较简单了,把所有的二进制位每16位进行一次加法,最后求一次补码(同样也要溢出回卷),如果结果全部是1,那就稳了,没出错;只要有一位是0,那就说明出现了错误。

Python实现

看完以上,你肯定懂了是怎么算的,但是代码实现可能还有点困难,这里特别感谢JamesF. Kurose ——《计算机网络:自顶向下方法》的作者在书中附带的资源和代码,以下代码来自书中编程实验的ICMPping程序。

具体的代码解析我注释好了,大家可以看看。

def checksum(str):
	csum = 0  # 校验和  (一个32位十进制数,因为每16位相加时可能会产生进位(即溢出),这些溢出将会被回卷)
	# 奇偶控制,如果总长的字节数为奇数时,肯定最后一个字节要单独相加(求校验和时是每16位一加)
	countTo = (len(str) // 2) * 2
	count = 0
	while count < countTo:
		# ord()函数返回一个字符的ASCII码
		# 取两个字节,第二个字节放在16位的高位,第一个字节放在16位的地位
		thisVal = (str[count + 1] << 8) + str[count]
		csum = csum + thisVal
		# 这里和0xffffffff进行and运算主要是为了保留每次运算过程中可能出现的16位溢出,
		# 这样一来,就可以将溢出位(也就是进位)保存到sum的高16位
		csum = csum & 0xffffffff
		count = count + 2  # 后移两个字节,也就是准备求和下一个16位
# 如果真的有一个字节剩余
if countTo &lt; len(str):
	csum = csum + str[len(str) - 1].decode()
	csum = csum &amp; 0xffffffff

# 把csum的高16位溢出回卷,加到低16位上
csum = (csum &gt;&gt; 16) + (csum &amp; 0xffff)
# 如果还产生了溢出,再操作一次
csum = csum + (csum &gt;&gt; 16)
# 求反码
answer = ~csum
answer = answer &amp; 0xffff
# 这里进行字节序大小端转换,因为网络字节序是大端模式
answer = answer &gt;&gt; 8 | (answer &lt;&lt; 8 &amp; 0xff00)
return answer
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

Reference

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值