C/C++实现crc码计算和校验浅谈

「爱情、让人受尽委屈。」 2024-03-22 22:03 154阅读 0赞

1e58881f6306a4f6008c9c1d2dc61659.jpeg

循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术。本文主要介绍了C++实现crc码计算和校验的方法,需要的可以参考一下

算法介绍

循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存后可能出现的错误。它是利用除法及余数的原理来作错误侦测的。

CRC校验计算速度快,检错能力强,易于用编码器等硬件电路实现。从检错的正确率与速度、成本等方面,都比奇偶校验等校验方式具有优势。因而,CRC 成为计算机信息通信领域最为普遍的校验方式。常见应用有以太网/USB通信,压缩解压,视频编码,图像存储,磁盘读写等

参数模型

CRC参数模型

不知道你是否遇到过这种情况,同样的CRC多项式,调用不同的CRC计算函数,得到的结果却不一样,而且和手算的结果也不一样,这就涉及到CRC的参数模型了。计算一个正确的CRC值,需要知道CRC的参数模型。

一个完整的CRC参数模型应该包含以下信息:WIDTH,POLY,INIT,REFIN,REFOUT,XOROUT。

NAME:参数模型名称。

WIDTH:宽度,即生成的CRC数据位宽,如CRC-8,生成的CRC为8位

POLY:十六进制多项式,省略最高位1,如 x8 + x2 + x + 1,二进制为1 0000 0111,省略最高位1,转换为十六进制为0x07。

INIT:CRC初始值,和WIDTH位宽一致。

REFIN:true或false,在进行计算之前,原始数据是否翻转,如原始数据:0x34 = 0011 0100,如果REFIN为true,进行翻转之后为0010 1100 = 0x2c

REFOUT:true或false,运算完成之后,得到的CRC值是否进行翻转,如计算得到的CRC值:0x97 = 1001 0111,如果REFOUT为true,进行翻转之后为11101001 = 0xE9。

XOROUT:计算结果与此参数进行异或运算后得到最终的CRC值,和WIDTH位宽一致。

接收端的校验有两种方式:

  • 一种是和CRC计算一样,在本地把接收到的数据和CRC分离,然后在本地对数据进行CRC运算,得到的CRC值和接收到的CRC进行比较,如果一致,说明数据接收正确,如果不一致,说明数据有错误。
  • 另一种方法是把整个数据帧进行CRC运算,因为是数据帧相当于把原始数据左移8位,然后加上余数,如果直接对整个数据帧进行CRC运算(除以多项式),那么余数应该为0,如果不为0说明数据出错

代码计算








1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

//求数的二进制最高位的幂指数,即MSB

static int getMinPolynomialBits(uint64_t n) {

    int r = 0;

    while (n >>= 1) r++;

    return r;

}

//append>0表示计算crc校验码,赋值到crcRemainder

//append=0,表示校验输入bit流是否正确;0表示正确,-1表示错误

//此处的多项式默认为0x96(高位补1后的结果),默认crc位数为7,可根据代码自行修改

static int crcCheck(const char msg, int append, char crcRemainder)

{

    if (msg == NULL || crcRemainder == NULL || strlen(msg) == 0) {

        printf(“input parameter is unvalid!\n”);

        return -1;

    }

     //hex: 0x96 = b’10010110’ = DEC:150

    uint64_t poly = 0x96;

    int polyLen = getMinPolynomialBits(poly + 1); //=7

    int msgLen = strlen(msg);

    //printf(“%d\n”, msgLen);

    //计算crc校验码

    if (append) {

        unsigned char pBufCrc = (unsigned char)calloc(msgLen + polyLen, sizeof(unsigned char));

        memset(pBufCrc, 0, msgLen + polyLen);

        for (int j = 0; j < msgLen; j++) {

            pBufCrc[j] = msg[j] - ‘0’;

        }

        uint8_t p = NULL;

        for (int i = 0; i < msgLen; i++) {

            if (pBufCrc[i]) {

                p = pBufCrc + i + polyLen;

                uint64_t t = poly;

                do {

                    (p—) ^= t & 1;

                } while (t >>= 1);

            }

        }

        p = NULL;

        size_t k;

        for (k = 0; k < polyLen; k++) {

            crcRemainder[k] = pBufCrc[k + msgLen] + 48;

        }

        if (pBufCrc) {

            free(pBufCrc);

            pBufCrc = NULL;

        }

    }

    else {

        // 校验接受端的比特流

        unsigned char pBuffer = (unsigned char)calloc(msgLen, sizeof(unsigned char));

        memset(pBuffer, 0, msgLen);

        int inforLen = msgLen - polyLen;//提取出信息流部分,然后计算当前信息对应crc校验码

        for (int j = 0; j < inforLen; j++) {

            pBuffer[j] = msg[j] - ‘0’;

        }

        uint8_t p = NULL;

        for (int i = 0; i < inforLen; i++) {

            if (pBuffer[i]) {

                p = pBuffer + i + polyLen;

                uint64_t t = poly;

                do {

                    (p—) ^= t & 1;

                } while (t >>= 1);

            }

        }

        p = NULL;

        //计算得到的crc码和输入的crc码进行对比验证,若每一位都相同,则校验成功

        for (size_t k = inforLen; k < msgLen; k++) {

            if (msg[k] != pBuffer[k] + 48) {

                if (pBuffer) {

                    free(pBuffer);

                    pBuffer = NULL;

                }

                return -1;

            }

        }

        if (pBuffer) {

            free(pBuffer);

            pBuffer = NULL;

        }

    }

    return 0;

}

到此这篇关于C/C++实现crc码计算和校验的文章就介绍到这了,希望可以帮到你。

转自:微点阅读 https://www.weidianyuedu.com

发表评论

表情:
评论列表 (有 0 条评论,154人围观)

还没有评论,来说两句吧...

相关阅读

    相关 CRC校验

    CRC校验码 简介 简介:本文讲解一个CRC校验码的题目。 CRC简介: 循环冗余校验码(CRC),简称循环码,是一种常用的、具有检错、纠错能力的校验码,在早

    相关 java使用CRC32计算校验

          校验和checkSum指的是通过一定的算法,将一个文件流转换为一串字母或数字,当文件流发生变化时,得到的校验和也会不同。      通常用来判断一个文件是否有修改