最近看谢希仁的《计算机网络》课本,里面出现了5处小错误(具体的我写到我的新浪微博里了,详见http://m.weibo.cn/1901976851/3983830128222967?sourceType=sms&from=1066095010&wm=20005_0002)最近又遇到一个问题,就是校验和字段,这个在IP协议和UDP里面都有提到,它们使用的是同一个算法(反码求和),只是IP校验的是IP首部,UDP校验的是伪首部+首部+数据。具体是有两个问题,按照字面含义,反码求和,不应该是先求反码,再求和,可是老谢的课本是先求和,最后对和求的反码,我在网上查了相关资料,百度上好多人回答的是先求反码再求和(当然百度的内容不能全信,你懂的),那么问题来了,运算顺序不一样,对结果会不会有影响。第二个问题是老谢的课本在IP那儿说如果接收方收到以后再进行一次反码求和,如果结果是全0,则没有差错,否则反之。在UDP也是同样的情形,接收方收到以后也要进行反码求和,如果结果是全1,则没有差错,否则反之。当然这个思路推理过来,似乎有些自相矛盾。(不过要是注意到细节的话,还是没有问题的)下来我们具体看一个例子,看看到底是什么样子的。

首先求反码没有问题,就是把1变成0,把0变成1。求和这一部分的话除了要知道0加0等于0,1加0等于1,1加1等于0,并向高位产生进位,如果已经是最高位了,它的进位该怎么处理,这时候要在原来结果上去加一个1(后面例子也会讲到),最初要把校验和字段置为全0。这个共识达成了以后,我们就开始今天的例子。

为了简化运算我们统一取4位2进制数表示,比如原始数据是15和8,转成四位二进制就分别是1111和1000,当然最初的校验和字段是0000。首先我们用第一种方法,先求反码,再求和。对于发送方,15的反码是0000,8的反码是0111,初始的校验和反码是1111,然后把0000和0111和1111相加,得到的结果为0111(本来是0110但是最高位产生了一个进位,所以要在0110的基础上加1)。所以最后得到的校验和为0111,并发送给接收方,接收方收到以后,也要对他们先求反码,再求和,15的反码是0000,8的反码是0111,校验和的反码是1000,把0000和0111和1000相加,得到的结果为1111(最高位没有进位),也就是结果是全1。然后我们先求和,最后对和求反码,15是1111,8是1000,最初的校验和是0000,我们对这三个数求和,结果是1000(本来是0111但是高位有一个进位,所以要加1)。最后对和1000取反码结果为0111,所以最后的校验和为0111,接收方收到 以后,也要对它们先求和,再求反码。就是把1111,1000,0111三个数相加的结果为1111(本来是1110,但是最高位有一个进位1,所以要加1),最后对结果取反,结果是0000,也就是全0。 具体过程见下图enter image description here 最终可以得出这样一个结论:不管是先求反码再求和,还是先求和再对和求反码,计算出来的校验和是相同的。但是对于差错的标准不同,一个全0证明无差错,一个要全1证明无差错。(当然老谢这种计算思路比较权威,就是先求和,再对和求反码)而且对于老谢这种先求和,再对和求反码的,最后无差错的标准是接收方计算出来的结果是全0。上面提到的课本里面的IP和UDP那个“自相矛盾”,仔细推敲的话还是没有问题的,在IP那儿接收方要对数据求和以后,再对和求反码,所以全0无差错。而UDP那儿的接收方对数据求和以后,没有提到对和求反码,(当然这个细节很难注意到)如果这个和就是最终结果,所以全1无差错,也是对的。