CRC检验

CRC(循环冗余检验)

先简单介绍一下CRC

CRC也就是循环冗余校验码,是计算机网络通信领域常用的校验码。循环冗余校验码包括一系列移位、相除等数据编码规则,其算法原理、算法程序的设计与分析,都可以通过相应的软件编码进行解决。循环冗余校验码是利用软件进行校验的算法,因此其检验速度很快,校验的误码率也较低,整个计算机网络通信的信息传输速度很高。CRC差错纠正控制法能够有效减少通信线路的误码率,得到的通信数据传输信息更准确。

在数据的传输过程中由于空间电磁环境复杂等原因,可能会产生误码,即某几位数据0变为1,或1变为0,导致接收端得到错误的数据。为了降低误码率,通常对数据进行特定编码,在收发端进行额外的验证,使接收端能发现某些错误,进而实现纠错功能,常用的编码方法有CRC-32校验码、CRC-16校验码、汉明码、奇偶校验法等。其中32位循环冗余校验简称CRC-32校验在性能和资源消耗两方面都有较大的优势,因而,在无线电通信、SATA硬盘数据传输等系统中,CRC-32校验是最常用的检错手段之一。(摘自百度百科)

用一个简单的例子引入我对CRC32的一些看法。

是以太网在产生帧校验序列时使用的生成多项式。

现在我给出如下的数据帧:

30 0D 9E 67 CF 70 70 20 84 03 95 48 08 00 45 00 00 3C 7B FE 00 00 40 01 00 00 C0 A8 6E 65 C0 A8 01 01 08 00 4D 21 00 01 00 3A 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 61 62 63 64 65 66 67 68 69

首先我们需要了解到的是:CRC32检验的“初始值”,“结果异或值”,“输入反转值”,“输出反转值”。

至于我们为什么要这么计算这里暂且不做讨论,我们把注意力放在计算的过程上从而理解CRC校验码的生成过程。

计算步骤如下:

1) 首先先将数据帧转换成二进制格式;

2) 每八位数据倒置;

3) 异或初始值

4) 因为的位阶是32,需要补32个0;

5) 对多项式进行模2除法;

6) 得到

7) 将与输出值异或;

8) 将异或值倒置;

那么我们首先将16进制的数据帧转换为二进制格式:

11000000001101100111100110011111001111011100000111000000100000100001000000001110010101010010000000100000000000010001010000000000000000001111000111101111111110000000000000000001000000000000010000000000000000110000001010100001101110011001011100000010101000000000010000000100001000000000000100110100100001000000000000000100000000001110100110000101100010011000110110010001100101011001100110011101101000011010010110101001101011011011000110110101101110011011110111000001110001011100100111001101110100011101010111011001110111011000010110001001100011011001000110010101100110011001110110100001101001

我们这样就得到了的初始值。

那么接下来就是对每八位数据进行倒置的操作。

例如30,他的二进制形式是11000000,八个数据为一byte;进行倒置操作后得到的是00000011。对上述的二进制数据全部进行这样的操作。完成后得到的输入反转值。

那么接下来我们需要进行的就是模2除法运算。

原理可以参考:(36条消息) 循环冗余校验(CRC,模2运算)_crc模二运算_dxz44444的博客-CSDN博客

因为检验的位阶是32,我们需要在输入反转值的基础上在数据末尾补上32个0(同理就是16个0,就是8个0)

然后与输入异或值进行异或操作

即对二进制数据的前32位进行取反操作。

那么我们得到了这样的一串二进制数据。

111100110100111110000110000110011111001100001110000011100000010000100001110000001010100100010010000100000000000010100010000000000000000000111100110111100111111100000000000000000000001010000000000000000000000000000011000101010111011010100110000000110001010110000000100000000001000000000000101100101000010000000000100000000000000001011100100001100100011011000110001001101010011001100110111001100001011010010110010101101101011000110110101101100111011011110110000011101000111001001110110011100010111010101110011011101110111010000110010001101100011000100110101001100110011011100110000101101001011000000000000000000000000000000000

该二进制数据作为除数,多项式作为除数,做模2除法运算。

得到

与输出异或值进行异或后得到:

转换为十六进制的话我们就能得到校验码了。

这里我是用C++实现的运算代码,可能会比较复杂。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include<bits/stdc++.h>

using namespace std;
#define CRC_32a "100000100110000010001110110110111"
unsigned char* mod2_add_sub(const unsigned char* num1, unsigned int length1, const unsigned char* num2,unsigned int length2)
{
unsigned char* res = static_cast<unsigned char *>(malloc(length1 > length2 ? length1 : length2));
if(length1 > length2)
{
for(unsigned int i = 0; i < length2; i++){
*res = (*num1) ^ (*num2);
res++;
num1++;
num2++;

}
memcpy(res, num1, length1 - length2);
}
else
{
for(unsigned int i = 0; i < length1; i++){
*res = (*num1) ^ (*num2);
res++;
num1++;
num2++;
}
memcpy(res, num2, length2 - length1);
}
return res - (length1<length2?length1:length2);
}
char mod2_add_sub(char a,char b)
{
//相当于异或
if((a=='0' &&b=='0')||(a=='1' &&b=='1'))
{
return '0';
}
else
{
return '1';
}
}

//模2除法
string mod2_division(string a,string b)
{
for(int i=0;i<b.length()-1;i++)
{
a=a+"0";
}
//长度
int alength=a.length();
int blength=b.length();
//运算次数
int n=alength-blength+1;

string shang=b;
//除法运算
for(int i=0;i<n;i++)
{
for(int k=0;k<blength;k++)
{
a[k]=mod2_add_sub(a[k],shang[k]);
}
a=a.substr(1);
if(a[0]=='0')
{
shang="";
for(int j=0;j<blength;j++)
{
shang=shang+"0";
}
}
else
{
shang=b;
}
}
return a;
}
//根据CRC冗余校验生成FCS
string FCS_CRC(string data)
{
string fcs="";
fcs=mod2_division(data,CRC_32a);
return fcs;
}
int main()
{
int o=0;
char e;
string hexDigit="300D9E67CF7070208403954808004500003C7BFE000040010000C0A86E65C0A8010108004D210001003A6162636465666768696A6B6C6D6E6F7071727374757677616263646566676869";
string crc_1[hexDigit.length()];
for(int f=0;f<=hexDigit.length();f++)//将16进制数转换为2进制
{
e=hexDigit[f];
if(e>='A'&&e<='F')
{
int a=static_cast<int>(e-'A'+10);
switch(a)
{
case 10:crc_1[o]="1010";o++;continue;
case 11:crc_1[o]="1011";o++;continue;
case 12:crc_1[o]="1100";o++;continue;
case 13:crc_1[o]="1101";o++;continue;
case 14:crc_1[o]="1110";o++;continue;
case 15:crc_1[o]="1111";o++;continue;
}
}
else if(isdigit(e))
{
int b=static_cast<int>(e-'0');
switch(b)
{
case 0:crc_1[o]="0000";o++;continue;
case 1:crc_1[o]="0001";o++;continue;
case 2:crc_1[o]="0010";o++;continue;
case 3:crc_1[o]="0011";o++;continue;
case 4:crc_1[o]="0100";o++;continue;
case 5:crc_1[o]="0101";o++;continue;
case 6:crc_1[o]="0110";o++;continue;
case 7:crc_1[o]="0111";o++;continue;
case 8:crc_1[o]="1000";o++;continue;
case 9:crc_1[o]="1001";o++;continue;
}
}
while(o==hexDigit.length())
{
break;
}
}

string crc_32[hexDigit.length()/2];
int x=0;
for(int j=0;j<hexDigit.length()/2;j++)
{

crc_32[j]=crc_1[x]+crc_1[x+1];
x=x+2;

}
unsigned int CRC_32[8];
unsigned int CRC_32MAX[624];
memset(CRC_32MAX,0,sizeof(CRC_32MAX));//将后32位补0
int u=0;
for(int j=0;j<hexDigit.length()/2;j++)
{
reverse(crc_32[j].begin(),crc_32[j].end());//每8位数据进行反转
string temp=crc_32[j];
for(int t=0;t<temp.length();t++)
{
stringstream b;
b<<temp[t];
b>>CRC_32[t];//将数据存入数组
}
if(j<4)
{
for (int t = 0; t < temp.length(); t++)//异或初始值0xFFFFFFFF
{
if (CRC_32[t] == 1) {
CRC_32[t] = 0;
} else {
CRC_32[t] = 1;
}
}
}
for(int t=0;t<temp.length();t++)
{
CRC_32MAX[u]=CRC_32[t];
u++;
}

}
for(int j=0;j<624;j++)
{
cout<<CRC_32MAX[j];
}
cout<<endl<<endl;//得到的数据放入下方的fcs中

string fcs="1111001101001111100001100001100111110011000011100000111000000100001000011100000010101001000100100001000000000000101000100000000000000000001111001101111001111111000000000000000000000010100000000000000000000000000000110001010101110110101001100000001100010101100000001000000000010000000000001011001010000100000000001000000000000000010111001000011001000110110001100010011010100110011001101110011000010110100101100101011011010110001101101011011001110110111101100000111010001110010011101100111000101110101011100110111011101110100001100100011011000110001001101010011001100110111001100001011010010110";
string FCS;
FCS=FCS_CRC(fcs);//求fcs
cout<<FCS<<endl;
}

CRC检验
http://example.com/2023/04/15/CRC检验/
作者
Jay
发布于
2023年4月15日
许可协议