• Checksum


    概念

    检验和 (checksum),在数据处理和数据通信领域中,用于 校验目的 的一组数据项的和,用来校验数据的完整性准确性

    一般的 checksum 计算方法

    1. 把要计算 checksum 的数据内容分成以每两个字节为一组的分组。如果最后剩余单个字节,补一个内容为 0 的字节。

      如数据:hello world

      String: hello world
              h         e         l         l         o        '空格'      w         o         r         l         d
      HEX:    68        65        6C        6C        6F        20        77        6F        72        6C        64
      BIN:    0110 1000 0110 0101 0110 1100 0110 1100 0110 1111 0010 0000 0111 0111 0110 1111 0111 0010 0110 1100 0110 0100
      

      分组:每16位一组

      h         e
      0110 1000 0110 0101
      l         l
      0110 1100 0110 1100
      o         '空格'
      0110 1111 0010 0000
      w         o
      0111 0111 0110 1111
      r         l
      0111 0010 0110 1100
      d         0          #补一个为 0 的字节
      0110 0100 0000 0000
      
    2. 将所有分组(16bit)累加起来,累加过程中,如果结果溢出(超过16bit),将溢出的位作为新的分组累加上去

                        h         e
                        0110 1000 0110 0101
                        l         l
      +                 0110 1100 0110 1100 
      -------------------------------------
      =                 1101 0100 1101 0001
                        o         '空格'
      +                 0110 1111 0010 0000
      -------------------------------------
      =               1 0100 0011 1111 0001  #溢出
      
      #累加溢出位
                        0100 0011 1111 0001
      +                 0000 0000 0000 0001  #溢出数据
      -------------------------------------
      =                 0100 0011 1111 0010
                        w         o
      +                 0111 0111 0110 1111 
      -------------------------------------
      =                 1011 1011 0110 0001
                        r         l
      +                 0111 0010 0110 1100 
      -------------------------------------
      =               1 0010 1101 1100 1101  #溢出
      
      #累加溢出位
                        0010 1101 1100 1101
      +                 0000 0000 0000 0001  #溢出数据
      -------------------------------------
      =                 0010 1101 1100 1110
                        d         0
      +                 0110 0100 0000 0000  #补一个为0的字节
      -------------------------------------
      =                 1001 0001 1100 1110
      ~
      -------------------------------------
                        0110 1110 0011 0001  #取反后的值即为 checksum
      hex:              6E31
      
    3. 将最终累加得到的16bit值按位取反,得到checksum的值

    1001 0001 1100 1110
    按位取反得到:0110 1110 0011 0001 ==> 6E31
    

    C 语言实现

    #include <stdio.h>
    #include <string.h>
    
    /**
     * 宿主机ubuntu18.04 x64是小端机器
     */
    #define _LITTLE_ENDIAN_
    //#define _BIG_ENDIAN_
    
    #if defined(_LITTLE_ENDIAN_)
    #define WORD_SWAP(v) ((unsigned short)(((v>>8)&0xff) | ((v<<8)&0xff00)))
    #elif defined(_BIG_ENDIAN_)
    #define WORD_SWAP(v) (v)
    #endif
    
    
    /**
     * @Name                 - 计算校验和
     * @Parameter *data      - 要计算校验和的数据,非空指针
     * @Parameter len        - 要计算校验和的数据长度
     */
    unsigned short cksum(unsigned char *data, int len)
    {
    	unsigned int sum = 0;
    	unsigned short cksum = 0;
    
    	while (len > 1) {
    		sum += (*((unsigned short *)data));
    		len -= 2;
    		data += 2;
    	}
    
    	if (len > 0) { /* 每两个字节分组后剩余 1 byte */
    	#if defined(_LITTLE_ENDIAN_)
    		sum += *data;
    	#elif defined(_BIG_ENDIAN_)
    		sum += ((*data << 8) & 0xff00);
    	#endif
    	}
    
    	/* 把溢出位累加到sum */
    	while(sum >> 16) {
    		sum = (sum & 0xffff) | (sum >> 16);
    	}
    
    	cksum = (unsigned short)sum;
    
    	return WORD_SWAP(~cksum);
    }
    
    
    int main(int argc, char *argv[])
    {
    	char *str = "hello world";
    	unsigned char data[] = { //hello world
    		0x68,0x65,
    		0x6C,0x6C,
    		0x6F,0x20,
    		0x77,0x6F,
    		0x72,0x6C,
    		0x64
    	};
    
    	printf("net byte order cksum = 0x%x\n", cksum(str, strlen(str)));
    	printf("net byte order cksum = 0x%x\n", cksum(data, sizeof(data)));
    
    	return 0;
    }
    
    

    run

    ┌──(shelmean㉿ubuntu)-[~/cksum]
    └─$ gcc cksum.c -o cksum
    ┌──(shelmean㉿ubuntu)-[~/cksum]
    └─$ ./cksum
    net byte order cksum = 0x6e31
    net byte order cksum = 0x6e31
    

    应用

    如 UDP 数据报中的应用

    image-20220402092306765

    1. 发送方在发送数据报之前,把 checksum 字段清空,然后计算整个报文的 checksum 值,把 checksum 值放入 checksum 字段
    2. 接收方收到这个数据之后,使用同样的 checksum 算法,计算整个 checksum,如果数据发送没有出错,则 checksum 计算出来应该是 0

    即:~(~checksumval + checksumval) = 0

    ~checksumval是源数据中checksum字段清空后的所有数据的累加和

    checksumvalchecksum字段的值

    ​ 两部分累加后取反即是收到的数据的checksum

    图示理解:

    cksum

    参考

    百度百科 校验和
    《Computer Networking A Top-Down Approach》

  • 相关阅读:
    (转) 将VB.NET网站转换成C#的全过程
    vb.net转换为C#方法
    (转)使用Microsoft Web Application Stress Tool对web进行压力测试
    (转)js 中{},[]中括号,大括号使用详解
    (转)js学习笔记()函数
    (转)几种HtmlEncode的区别
    编译Redis系统提示缺少gcc,可以使用yum进行安装:
    linux如何关闭防火墙
    Linux less命令简介
    Linux unzip解压文件到某个目录下面
  • 原文地址:https://www.cnblogs.com/shelmean/p/16091622.html
Copyright © 2020-2023  润新知