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' ; } }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; }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++) { 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)); int u=0 ; for (int j=0 ;j<hexDigit.length ()/2 ;j++) { reverse (crc_32[j].begin (),crc_32[j].end ()); 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++) { 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; string fcs="1111001101001111100001100001100111110011000011100000111000000100001000011100000010101001000100100001000000000000101000100000000000000000001111001101111001111111000000000000000000000010100000000000000000000000000000110001010101110110101001100000001100010101100000001000000000010000000000001011001010000100000000001000000000000000010111001000011001000110110001100010011010100110011001101110011000010110100101100101011011010110001101101011011001110110111101100000111010001110010011101100111000101110101011100110111011101110100001100100011011000110001001101010011001100110111001100001011010010110" ; string FCS; FCS=FCS_CRC (fcs); cout<<FCS<<endl; }