Checksum

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
    0110 0100
  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 <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <endian.h>
#include <arpa/inet.h>
/**
* @Name - 计算校验和
* @Parameter *data - 要计算校验和的数据,非空指针
* @Parameter len - 要计算校验和的数据长度
*/
uint16_t cksum(unsigned char *data, int len)
{
unsigned int sum = 0;
uint16_t cksum = 0;
uint16_t tval = 0;
/* 每两个字节进行累加 */
while (len > 1) {
sum += (*((uint16_t *)data));
len -= sizeof(uint16_t);
data += sizeof(uint16_t);
}
if (len > 0) { /* 每两个字节分组后剩余 1 byte */
*((unsigned char *)&tval) = *data;
sum += tval;
/* 两种方式均可 */
// #if __BYTE_ORDER == __LITTLE_ENDIAN
// sum += *data;
// #else
// sum += ((*data << 8) & 0xff00);
// #endif
}
/* 把溢出位累加到sum */
while(sum >> 16) {
sum = (sum & 0xffff) + (sum >> 16);
}
cksum = sum & 0xffff;
return (~cksum);
}
int main(int argc, char *argv[])
{
uint16_t csum = 0;
char *str = "hello world";
unsigned char data[] = { //hello world
0x68,0x65,
0x6C,0x6C,
0x6F,0x20,
0x77,0x6F,
0x72,0x6C,
0x64
};
csum = cksum(str, strlen(str));
printf("cksum=0x%x net byte order cksum = 0x%x\n", csum, htons(csum));
csum = cksum(data, sizeof(data));
printf("cksum=0x%x net byte order cksum = 0x%x\n", csum, htons(csum));
return 0;
}

run

小端机上编译运行:
❯ ./a.out
cksum=0x316e net byte order cksum = 0x6e31
cksum=0x316e net byte order cksum = 0x6e31
大端机上编译运行:
# ./endian
cksum=0x6e31 net byte order cksum = 0x6e31
cksum=0x6e31 net byte order cksum = 0x6e31

应用

如UDP数据报中的应用

image-20220402092306765

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

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

​ checksumval是checksum字段的值

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

​ 即:(checksum + checksum) = 0

图示理解:

cksum

posted @   shelmean  阅读(920)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程
点击右上角即可分享
微信分享提示