计算机网络学习笔记——IP Header Checksum(校验和)的计算方法

从TCP/IP协议看到IP数据报,看到Checksum的算法描述,不甚了了。

The checksum field is the 16 bit one’s complement of the one’s complement sum of all 16 bit words in the header. ————RFC791

1、怎么算IP Header Checksum?

百度百科里对校验和的解释提到了:1的补码和(one’s complement sum)就是带循环进位(end round carry)的加法,在最后对累加结果取反码。
其实这里讲得很清楚了,但不完全清楚。因为对1的补码和到底怎么算,理解错了。

  1. 最开始是按照学数电时对反码加法的理解,即正数的反码等于原码,负数的反码由原码的按位(除了符号位保持为1)取反得到。错。
  2. 百科最后有附的程序,感觉第10行代码是迷之取反。按这个思路求的结果也不对。

百科所附C语言程序

  1. 找到一个求IP Header Cheaksum的正确方法
    1. 把划分出所有的16bit数据逐个相加,一旦有进位,就把它加到最低位。
    2. 最后把结果所有位按位取反。
  2. 又找到一个等价的方法:IP数据报首部校验和算法 详细 非代码(接收方检验结果有笔误,但总体写得很棒!)
    1. 所有数据逐个相加(使用32位的int类型)
    2. 把高16位右移16位,再与低16位相加。循环该步,直至结果的二进制表示不超过16位。

2、反码究竟是什么?怎么可以这样用?

可行的方法找到了,可为什么是这样的呢?就像这篇博客里说的那样,这些16bit数据谈不上有正负号,没有什么符号位,一律按位取反,跟加减算数里的用法相去甚远。

首先根据一下表格明确各个概念:

名称英文名称直译名称
反码one’s complement1的补码
补码two’s complement2的补码

然后看一下维基百科对ones’ complement的解释

The ones’ complement of a binary number is the value obtained by inverting all the bits in the binary representation of the number (swapping 0s and 1s). This mathematical operation is primarily of interest in computer science, where it has varying effects depending on how a specific computer represents numbers.
A ones’ complement system or ones’ complement arithmetic is a system in which negative numbers are represented by the inverse of the binary representations of their corresponding positive numbers. In such a system, a number is negated (converted from positive to negative or vice versa) by computing its ones’ complement. An N-bit ones’ complement numeral system can only represent integers in the range −(2N−1−1) to 2N−1−1 while two’s complement can express −2N−1 to 2N−1−1. It is one of three common representations for negative integers in microprocessors, along with two’s complement and sign-magnitude.
The ones’ complement binary numeral system is characterized by the bit complement of any integer value being the arithmetic negative of the value. That is, inverting all of the bits of a number (the logical complement) produces the same result as subtracting the value from 0.

第一段黑体处明确指出1补是所有位按位取反,
第二段黑体部分说1补运算系统中负数表示为对应正数的反码。

由此可知,1补就是所有位取反的一个操作,应用在运算中可以方便负数的表示和运算,但这并非其全部用法

3、反码求和为什么循环进位?

反码求和(one’s complement sum):循环进位。先求和再取反,与先取反再求和,结果是一样的。
那反码求和为什么是循环进位呢?
可以查看问题 为什么二进制反码加减要循环进位?为什么补码加减不需要循环进位?下的回答。其中有分类讨论,讲得非常棒!
最后也猜了下Checksum使用1补的原因。

4、接收端怎么检验,正常结果应该是什么?

接收端收到IP数据报的时候也这么算。
因为Checksum存了其他数据的和的反码(Checksum初始化为0x0000,虽然参加了运算,但跟没参加一样),其他数据之和加上本身的反码,就是0xFFFF,即-0,再取反就是0x0000,即+0
(补充:反码表示负数时,有两个零,补码只有一个零,可以比反码多表示一个负数)

5、Java代码简单实现

java代码如下:

package cn.it.test;

public class CheckSum {
    public static void main(String[] args) {
        int[] msg = {0x9C1A, 0xDA88, 0xAD35, 0x0000};
        System.out.println(checkSum(msg));
    }

    /**
     * IP Header Checksum
     * @param msg 数组中的每一个数都是16bit的
     * @return 返回16进制的字符串
     */
    public static String checkSum(int[] msg) {
    	// one's complement sum 带循环进位的加法
        int sum = 0;
        for (int m : msg) {
            sum += m;
            if (sum > 0xFFFF) {
                sum &= 0xFFFF;
                ++sum;
            }
        }
		
		// one's complement 每一位都按位取反
        sum = ~sum;
        sum &= 0xFFFF;

        return String.format("0x%04x",sum);
    }
}

6、补充简洁的解释

后来查1补的用法时,看到这个回答,非常简洁。

一个例子就是求校验和(Checksum) 通俗一点来讲是:

  1. 先是 one’s complement sum,将data按16位分组,所有组相加,再将进位值加到结果的末位。
  2. 再是 one’s complement,结果取反码。

7、总结

  1. 会求Checksum了
  2. 打破了之前对反码的刻板认识