代码编织梦想

原文链接:http://blog.chinaunix.net/uid-26758209-id-3146230.html

校验和算法

    经常看计算机网络相关的书时,每次看到关于IP或者是UDP报头校验和时,都是一笑而过,以为相当简单的东西,不就是16bit数据的相加吗!最近在学习Ping命令的源待时,看到里面有关于校验和的算法。一头雾水,后来查找资料,看到校验和是16bit字的二进制反码和。总是觉得很奇怪,为什么会用反码和,而不是直接求和呢?或者是补码和呢?因为在计算机里面数据是以补码的形式存在啊!经过看书查资料,下面总结一些这个校验和算法具体是怎么实现的。

    首先,IP、ICMP、UDP和TCP报文头都有检验和字段,大小都是16bit,算法基本上也是一样的。

    在发送数据时,为了计算数据包的检验和。应该按如下步骤:

    1、把校验和字段设置为0;

    2、把需要校验的数据看成以16位为单位的数字组成,依次进行二进制反码求和;

    3、把得到的结果存入校验和字段中

    在接收数据时,计算数据包的检验和相对简单,按如下步骤:

    1、把首部看成以16位为单位的数字组成,依次进行二进制反码求和,包括校验和字段;

    2、检查计算出的校验和的结果是否为0;

    3、如果等于0,说明被整除,校验和正确。否则,校验和就是错误的,协议栈要抛弃这个数据包。

 

    虽然说上面四种报文的校验和算法一样,但是在作用范围存在不同:IP校验和只校验20字节的IP报头;而ICMP校验和覆盖整个报文(ICMP报头+ICMP数据);UDP和TCP校验和不仅覆盖整个报文,而且还有12个字节的IP伪首部,包括源IP地址(4字节)、目的IP地址(4字节)、协议(2字节)、TCP/UDP包长(2字节)。另外UDP、TCP数据报的长度可以为奇数字节,所以在计算校验和时需要在最后增加填充字节0(填充字节只是为了计算校验和,可以不被传送)。

    在UDO传输协议中,校验和是可选的,当校验和字段为0时,表明该UDP报文未使用校验和,接收方就不需要校验和检查了!那如果UDP校验和的计算结果是0时怎么办?书上有一句话:“如果校验和的计算结果为0,则存入的值为全1(65535),这在二进制反码计算中是等效的”

 

那么校验和到底怎么计算了?

1、什么是二进制反码求和

    对一个无符号的数,先求其反码,然后从低位到高位,按位相加,有益处则向高位进1(和一般的二进制法则一样),若最高位有进位,则向最低位进1.

    首先这里的反反码好像和以前学的有符号反码不一样,这里不分正负数,直接每个为都取反。

    上面加粗的那句话和我们平时的加法法则不一样,最高位有进位,则向最低位进1。确实有些疑惑,为什么要这样呢?自习分析一下,上面的这种操作,使得在发送加法进位溢出时,溢出值并不是10000,而是1111.也即是当相加结果满1111时溢出,这样也可以说明为什么0000和1111都表示0了。

    下面是两种二进制反码求和的运算:

    原码加法运算:3(0011)+5(0101)=8(1000)

                  8(1000)+9(1001)=1(0001)

    反码加法运算:3(1100)+5(1010)=8(0111)

                  8(0111)+9(0110)=2(1101)

    从上面的例子中,当加法未发生溢出时,原码与反码加法运算结果一样;当有溢出时,结果就不一样了,原码是满10000溢出,而反码是满1111溢出,所以相差正好是1.

    另外,关于二进制反码求和运算需要说明的一点是,先取反后相加与先相加后取反,得到的结果是一样的。

2、校验和算法实现

    代码如下:

    USHORT checksum (USHORT *buffer,int size)

    {

        Unsigned long cksum=0;

        While (size>1)

        {

            Cksum +=*buffer++;

            size -=sizeof(USHORT);

        }

        If (size)

        {

            Cksum +=*(UCHAR *) buffer;

        }

        //将32位转换为16位

        While (cksum>>16)

            Cksum = (cksum>>16) + (cksum & 0xffff);

        Return (USHORT) (~cksum);

    }

    buffer是指向需要校验数据缓冲区的指针,size是需要检验数据的总长度(字节为单位)。

    4-13行代码是对数据按16bit累加求和,由于最高位的进位需要加在最低位上,所以cksum必须是32位的unsigned long型,高16bit用于保存累加过程中的进位;另外代码10~13行是对size为奇数情况的处理。

    14~16行代码的作用是将cksum高16bit的值加到低16bit上,即把累加中最高位的进位加到最低位上。这里使用了while循环,判断cksum高16bit是否非零,因为第16行代码执行的时候,还是可能向cksum的高16bit进位。

    有些地方是通过下面两条代码实现的:

    Cksum = (cksum >> 16) + (cksum & 0xffff);

    Cksum  += (cksum >> 16);

    这里只进行了两次相加,即可保证相加后cksum的高16位为0,两种方式的效果是一样,事实上,上面的循环也最多执行两次!

    17行代码即对16bit数据累加的结果取反,得到二进制反码求和的结果,然后函数返回该值。

    3、为什么使用二进制反码求和呢?

    为什么要使用二进制反码来计算校验和呢,而不是直接使用原码或者是补码呢?

    在谷歌上找到一篇相关的文章:

    

    上面是原文的一部分,说明在TCP/IP校验和中使用反码求和的一些优点:

a、 不依赖系统是大端小端。即无论你是发送方计算机或者接收方检查校验和时,都不要调用htons或者ntohs,直接通过上面的算法就可以得到正确的结果。这个问题你可以自己举个例子,用反码求和时,交换16位数的字节顺序,得到的结果相同,只是字节顺序相应地也交换了;而如果使用原码或者补码求和,得到的结果可能就不同。

b、 计算和验证校验和比较简单、快递。

校验和-爱代码爱编程

校验和是用于检测传输过程中可能产生的错误,将其置于数据后,随数据一同发送,接收端通过同样的算法进行检查,若正确就接受,错误就丢弃 校验和C源代码: unsigned short checksum(unsigned char *buf,int len) {         unsigned int sum=0;          //1        

校验和计算原理-爱代码爱编程

校验和思路 首先,IP、ICMP、UDP和TCP报文头都有检验和字段,大小都是16bit,算法基本上也是一样的。 在发送数据时,为了计算数据包的检验和。应该按如下步骤: 1、把校验和字段设置为0; 2、把需要校验的数

二进制反码求和、原码、补码、逐位相“与”、逐位相“或”_y_momo_的博客-爱代码爱编程_二进制反码求和

       首先,数在计算机中是以二进制形式表示的;数分为有符号数和无符号数;原码、反码、补码都是有符号定点数的表示方法;无符号数全部按正数处理;一个有符号定点数的最高位为符号位,0是正,1是负。 一、二进制反码求和 规则: 从低位到高位逐列进行计算。0和0相加是0,0和1相加是1,1和1相加是0但要产生一个进位1,加到下一列。 即0+0=0;0

ip数据报二进制反码求和算法_鸿都客的博客-爱代码爱编程_ip数据报二进制反码求和算法

计算数据报的IP校验和,首先把校验和字段(16bit)置为0.然后,对首部(20字节)中每个16bit进行二进制反码求和,结果存在校验和字段中.当收到一份IP数据报后,同样对首部中每个16bit进行二进制反码求和.接收方在计

校验和计算方法-爱代码爱编程

1.说明:   [1]校验和覆盖的内容:     IP校验和:IP首部。     ICMP校验和:ICMP首部+ICMP数据;     UDP、TCP校验和:首部+数据+12个字节伪首部(源IP地址、目的IP地址、协议、TCP/UDP包长)。 2.计算校验和的步骤:   [1]把校验和字段设置为0。   [2]把需要校验的数据看成以16位为单

校验和的计算方法-爱代码爱编程

实验要求 编写一个计算机程序用来计算一个文件的16位效验和。最快速的方法是用一个32位的整数来存放这个和。记住要处理进位(例如,超过16位的那些位),把它们加到效验和中。 要求: 1)以命令行形式运行:check_sum infile 其中check_sum为程序名,infile为输入数据文件名。 2)输出:数据文件的效验和 附:效验和(che

IP头中的校验和计算方法介绍-爱代码爱编程

校验和的作用   按照协议的规定,报文到达每一层,首先验证校验和是否正确,丢弃掉不正确的报文,再才会进行后续操作。  那么校验和是怎么计算的呢?  校验和的计算方法(以 IP 首部中的校验和为例) 方法是计算16位的二进制和,首先将校验和字段(16位)置0,将每个16位相加,若最后未到16字节,则以0补充,然后对结果取反,结果存在校验和字段中,因接收端包含

c语言累加和校验_累加和校验算法(CheckSum算法)-爱代码爱编程

因为外界总会对电路存在或多或少的干扰,对于数字信号,很可能导致传输的数据出现千差万别。对于很多需要传输数据的场合,尤其是一些数据可能会影响一些硬件的动作(诸如嵌入式的一些设备、机器人等),错误的数据可能会带来一些隐性风险,想想都可怕。 由于本人是嵌入式相关领域的,平时玩的都是单片机,当然单片机的性能千差万别,不过很多的性能都只能说是勉强够用,毕竟成本

完整性校验用到常见的算法_数据校验的六种方式-转载-爱代码爱编程

一、为什么要进行数据校验 数据校验是为保证数据的完整性,用一种指定的算法对原始数据计算出的一个校验值。接收方用同样的算法计算一次校验值,如果和随数据提供的校验值一样,说明数据是完整的。 二、校验方法 1、最简单的校验 最简单的校验就是把原始数据和待比较数据直接进行比较,看是否完全一样这种方法是最安全最准确的。同时也是效率最低的。 例如:龙珠c

二进制反码求和java_简单又复杂的“整数类型”-爱代码爱编程

前言 因为一道题目让我不断地深追下去,挖出了我多年的噩梦——数据类型的范围与长度。每次都想得头痛,因为平台不同、编译器不同、编程语言不同等等因素,又没去做实验,网上那么多说法该相信谁都不知道……那不如趁现在就来详细地解决掉它吧。 一、原码、反码和补码 基础知识 相信在大学的《数字逻辑》课上都学过这个内容了,原码、反码和补码都是基于二进制而言的:

java反码算术运算求和,位运算的妙用,运算妙用-爱代码爱编程

位运算的妙用,运算妙用 最近在学java,其实仅仅是在命令行里写程序跟C语言没有太大的区别,思想都是一样的。遇到了一个比较新鲜(后来知道原来C中也有)的东西——位元算(又叫位操作)。多新鲜啊,毕向东老师说这次(现在正在学习的这次)使用位元算符将会是你们今后使用的唯一的一次,因为在开发中根本用不着。其实也差不多。不过见到了几个应用,还是蛮有意思的,这里