首先,我当你看过书,但是比较懵。
1,实例说明Checksum(校验和)的计算步骤
直奔主题,分析一下这个Checksum(校验和)怎么算出来的。
先用Wireshark随便抓一个UDP或TCP包分析一下。
如上面,我们得到IP帧头部实际数据(十六进制):
45 00 00 34 fd 34 40 00 80 11 79 46 c0 a8 63 64 31 07 2f 2a
我们看到Wireshark分析出来的Header Checksum是0x7946,下面我们计算验证一下。
Step1:根据IPv4头部格式,我们知道第11和12个字节是要填写的Checksum,先把这两个字节都设置为0,得到
45 00 00 34 fd 34 40 00 80 11 00 00 c0 a8 63 64 31 07 2f 2a
Step2:每两个字节组成一个数字,然后累加
4500 + 0034 + fd34 + 4000 + 8011 + 0000 + c0a8 + 6364 + 3107 + 2f2a =3 86B6
提示:可以把整个算式连同等号粘贴到Windows计算器(程序员模式,HEX方式)能得到结果。
Step3:把后面两个字节组成的数字86B6 和 进位3相加,上面的 3 86B6,分开两个数相加就是
3 + 86B6 = 86B9,一般是写成 86B6 + 3 = 86B9
Step4:取反
~(86B9)=~(1000 0110 1011 1001)=0111 1001 0100 0110=7946
(十六进制!)
所以得到Checksum是0x7946 是对的。
上面是发送端计算出checksum的过程。
下面是接收端校验的过程:
接收到得到数据:
45 00 00 34 fd 34 40 00 80 11 79 46 c0 a8 63 64 31 07 2f 2a
同样也是先把checksum两个字节先忽略(当0000),然后每两个字节组成一个数,累加:
4500 + 0034 + fd34 + 4000 + 8011 + 0000 + c0a8 + 6364 + 3107 + 2f2a =3 86B6
进位数加到低位,再变成
3 + 86B6 = 86B9
注:上面几个步骤跟发送端是一样的。
然后再加上checksum这个数:
86B9
+7946=FFFF取反:~(FFFF)= 0000
结果是0000就证明没错。
2,解惑时刻
这本书举例说的补码、反码(如下图),跟我们开始接触计算机学到的补码、反码是两码事!
或者你可以这么理解,这里说的数字都是无符号整数,正整数的补码就是自己,所以这里说的补码根本不是什么补码,就是本身;
这里说的反码,只是简单的按位取反!按位取反!(不是以前学的,正整数的反码是自身,不是.)
关键是求和的时候,是每16位(2个字节)组成一个数字 再累加的!
1E4FF怎么得来的?就是E34F+2396+4427+99F3+0000=1E4FF
接下来为什么 E4FF + 1 不是 E4FF + 2 ?因为刚才算的结果是1E4FF,进位数是1,如果得到的结果是9E4FF,那这一步就是 E4FF + 9。(那为什么要这么加呢?这就要搬出阿贝尔群了)
接下来就是取反操作了。
大家可以看下IETF的文档,里面甚至有C语言示例代码。
RFC 1071 - Computing the Internet checksum
3,阿贝尔群(Abelian Group)
这里只是顺便提一下。阿贝尔群概念相对简单,就是满足一般群的4个公理,又满足交换律公理:
交换性(Commutativity):对于G 中任意两个元素a,b, 满足a⋅b = b⋅a。
这就OK了,阿贝尔群又叫交换群。
对于群的概念,要注意理解的是,中间点"⋅"运算符虽然被叫为"乘法",实际上,它只是代表一种运算,可以是加法,也可以是乘法,或者减法、位运算
至于上面Checksum背后的数学性质与阿贝尔群的关系,书上有解释,在此不赘述。
书中有句话:对于16位的十六进制值 集合V = { 0001, ..., FFFF } 与其反码和运算 "+"共同形成一个阿贝尔群。
——这句话的说明了,定义" + "为 二进制反码和 运算,这个很关键。同样,这里的反码是按位取反的意思。
——书上说:对于V中的任何X,e + X = X + e = X,其中 e = FFFF。为什么呢?这里面有一步很关键的操作就是,进位数要加到低位去,举个栗子(都是十六进制数为例):FFFF + 0001 = 10000,进位为1,加到0000,结果就是0001,这才满足e + X = X (e = FFFF),不然你打死都不明白。
——这里吐槽一下书上把group翻译为组是不对的,正确的是“群”。