通讯协议中的checksum校验和算法

1 前言

说到检验和算法,比较熟悉的就是循环冗余算法(CRC),通常由CRC-8CRC-16,以及CRC-32等,但是在资源相对比较紧张的一些平台上,运行CRC也比较吃力,或者对于需要进行快速校验的场合,所以这里可以使用简单的Checksum算法。

2 具体思路

这里以8位的Checksum为例,假设输入一组数据0XFF0X050XC10X19

  • 16位变量保存数据的累加和;
  • 得到累积和之后,将累加和的高8位和低8位相加;
  • 将累加和进行取反操作;

整体流程如下图所示;

最终结算得到的结果为0x20,下面会有相应的测试。

3 checksum的算法实现

3.1 checksum8

uint8_t m_checksum_08(const uint8_t *pdata, uint32_t count)
{
    register uint16_t sum = 0;
    uint8_t* addr = (uint8_t *)pdata;
    if(count <= 0){
        goto error;
    }
    // Main summing loop
    while(count >= 1)
    {
        sum = sum + *(uint8_t *) addr;
        count --;
        (uint8_t *) addr++;
    }

    // Add left-over byte, if any
    if (count > 0)
    sum = sum + *((uint8_t *) addr);

    // Fold 16-bit sum to 8 bits
    while (sum>>8){
        sum = (sum & 0xFF) + (sum >> 8);
    }

    return (uint8_t)(~sum);
    error:
    return 0;

}

3.2 checksum16

uint16_t m_checksum_16(const uint8_t *pdata, uint32_t count)
{
    register uint32_t sum = 0;
	uint16_t* addr = (uint16_t *)pdata;
    if(count <= 0){
        goto error;
    }
    // Main summing loop
    while(count >= 2)
    {
        sum = sum + *((uint16_t *) addr);
        count = count - 2;
        (uint16_t *) addr++;
	    //addr = addr + 2;
    }
    if(count == 1){
        addr++;
        sum = sum + *((uint8_t *) addr);
    }

    // Add left-over byte, if any
    if (count > 0)
    sum = sum + *((uint8_t *) addr);

    // Fold 32-bit sum to 16 bits
    while (sum>>16){
         sum = (sum & 0xFFFF) + (sum >> 16);
    }

    return(~sum);
    error:
    return 0;
}

4 linux中的校验和算法

下面是Linux中作为IP/TCP/UDP的校验和算法的例程,可以在linux/lib/checksum.c中找到,具体如下所示;

#include <linux/export.h>
#include <net/checksum.h>
#include <asm/byteorder.h>
#ifndef do_csum
static inline unsigned short from32to16(unsigned int x)
{
	/* add up 16-bit and 16-bit for 16+c bit */
	x = (x & 0xffff) + (x >> 16);
	/* add up carry.. */
	x = (x & 0xffff) + (x >> 16);
	return x;
}

static unsigned int do_csum(const unsigned char *buff, int len)
{
	int odd;
	unsigned int result = 0;
	if (len <= 0)
		goto out;
	odd = 1 & (unsigned long) buff;
	if (odd) {
#ifdef __LITTLE_ENDIAN
		result += (*buff << 8);
#else
		result = *buff;
#endif
		len--;
		buff++;
	}
	if (len >= 2) {
		if (2 & (unsigned long) buff) {
			result += *(unsigned short *) buff;
			len -= 2;
			buff += 2;
		}
		if (len >= 4) {
			const unsigned char *end = buff + ((unsigned)len & ~3);
			unsigned int carry = 0;
			do {
				unsigned int w = *(unsigned int *) buff;
				buff += 4;
				result += carry;
				result += w;
				carry = (w > result);
			} while (buff < end);
			result += carry;
			result = (result & 0xffff) + (result >> 16);
		}
		if (len & 2) {
			result += *(unsigned short *) buff;
			buff += 2;
		}
	}
	if (len & 1)
#ifdef __LITTLE_ENDIAN
		result += *buff;
#else
		result += (*buff << 8);
#endif
	result = from32to16(result);
	if (odd)
		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
out:
	return result;
}
#endif

5 测试

下面使用三组数据进行测试,代码如下所示;


uint8_t data01[] = { 0XFF,0X05,0XC1,0X19 };
uint8_t data02[] = { 0X11,0X12,0X12,0X04 };
uint8_t data03[] = { 0X11,0X12,0X12,0X04 };

int main()
{
    printf("test\r\n");
    printf("data01 is 0x%02x \r\n",m_checksum_08(data01,sizeof(data01)));
    printf("data02 is 0x%02x \r\n",m_checksum_08(data02,sizeof(data02)));
    printf("data03 is 0x%02x \r\n",m_checksum_08(data03,sizeof(data03)));
    
    printf("data01 is 0x%04x \r\n",m_checksum_16(data01,sizeof(data01)));
    printf("data02 is 0x%04x \r\n",m_checksum_16(data02,sizeof(data02)));
    printf("data03 is 0x%04x \r\n",m_checksum_16(data03,sizeof(data03)));

    return 0;
}

测试结果如下,符合预期的效果;

猜你喜欢

转载自blog.csdn.net/u010632165/article/details/107437096