首先介绍一下1的补码,2的补码:(摘自http://blog.csdn.net/cradmin/article/details/3092559)
过1的补码,2的补码,到网上搜了下找到这个:
It is the 1’s complement of the 1’s complement sum of all the 16-bit words in the TCP header and data 这是关于TCP头部校验和字段(checksum field)的说明。
补码:补码是计算机中二进制数表达负数的办法,这样可以在计算机中把两个数的减法变成加法。补码形式有1的补码和2的补码,其中1的补码用在IP、TCP的校验和中;
The checksum algorithm is simply to add up all the 16-bit words in one‘s complement and then to take the one‘s complement of the sum.
1‘s Complement Arithmetic
The Formula
~N = (2^n -1) - N
where: n is the number of bits per word
N is a positive integer
~N is -N in 1‘s complement notation
For example with an 8-bit word and N = 6, we have:
~N = (2^8 -1) - 6 = 255 - 6 = 249 = 11111001
In Binary
An alternate way to find the 1‘s complement is to simply
take the bit by bit complement of the binary number.
For example: N = +6 = 00000110
N = -6 = 11111001
Conversely, given the 1‘s complement we can find the
magnitude of the number by taking it‘s 1‘s complement.
The largest number that can be represented in 8-bit 1‘s
complement is 01111111 = 127 = 0x7F. The smallest is
10000000 = -127. Note that the values 00000000 and
11111111 both represent zero.
Addition
End-around Carry. When the addition of two values
results in a carry, the carry bit is added to the sum in the
rightmost position. There is no overflow as long as the
magnitude of the result is not greater than 2^n-1.
2‘s Complement Arithmetic
The Formula
N* = 2^n - N
where: n is the number of bits per word
N is a positive integer
N* is -N in 2‘s complement notation
For example with an 8-bit word and N = 6, we have:
N* = 2^8 - 6 = 256 - 6 = 250 = 11111010
In Binary
An alternate way to find the 2‘s complement is to start at
the right and complement each bit to the left of the first
"1".
For example: N = +6 = 00000110
N* = -6 = 11111010
Conversely, given the 2‘s complement we can find the
magnitude of the number by taking it‘s 2‘s complement.
The largest number that can be represented in 8-bit 2s
complement is 01111111 = 127. The smallest is
10000000 = -128.
Addition
When the addition of two values results in a carry, the
carry bit is ignored. There is no overflow as long as the
is not greater than 2^n-1 nor less than -2^n.
我们之前学的是2的补码:正数补码是自己,负数补码=反码+1
而1的补码:正数补码是自己,负数的补码=各位取反(也就是反码)
IP/TCP首部校验规则:(摘自:http://www.cnblogs.com/fhefh/archive/2011/10/18/2216885.html,这篇讲解非常棒,有例子)
P/ICMP/IGMP/TCP/UDP等协议的校验和算法都是相同的,算法如下:
在发送数据时,为了计算数IP据报的校验和。应该按如下步骤:
(1)把IP数据报的首部都置为0,包括校验和字段。
(2)把首部看成以16位为单位的数字组成,依次进行二进制反码求和。
(3)把得到的结果存入校验和字段中。
在接收数据时,计算数据报的校验和相对简单,按如下步骤:
(1)当接收IP包时,需要对报头进行确认,检查IP头是否有误,算法同上2、3步,然后判断取反的结果是否为0,是则正确,否则有错。
1、发送方
i)将校验和字段置为0,然后将IP包头按16比特分成多个单元,如包头长度不是16比特的倍数,则用0比特填充到16比特的倍数;
ii)对各个单元采用反码加法运算(即高位溢出位会加到低位,通常的补码运算是直接丢掉溢出的高位),将得到的和的反码填入校验和字段;
iii)发送数据包。
2、接收方
i)将IP包头按16比特分成多个单元,如包头长度不是16比特的倍数,则用0比特填充到16比特的倍数;
ii)对各个单元采用反码加法运算,检查得到的和是否符合是全1(有的实现可能对得到的和会取反码,然后判断最终值是不是全0);
iii)如果是全1则进行下步处理,否则意味着包已变化从而丢弃之。需要强调的是反码和是采用高位溢出加到低位的,如3比特的反码和运算:100b+101b=010b(因为100b+101b=1001b,高位溢出1,其应该加到低位,即001b+1b(高位溢出位)=010b)
程序:
1 unsigned short checksum(unsigned short *buf, int nword) 2 { 3 // short是16位(俩字节),即把报表首部按16bit分开 4 // 先求和 5 unsigned long sum = 0; 6 for(int i=0; i<nword; i++) 7 { 8 sum += *buf++; 9 } 10 //高位溢出加到低位 11 sum = (sum >> 16) + (sum & 0xffff); 12 //防止上次操作引起的新溢出 13 sum += (sum >> 16); 14 //long(32 bit) 截取低位部分转换为short(16位) 15 return (unsigned short)~sum; 16 }