一、什么是CRC校驗算法
最近在學網絡時在以太網的數據幀的末尾有一個叫CRC校驗碼的東西,遂不解。于是便一起學習一下,什么是CRC校驗碼。
CRC就是循環冗余校驗碼(Cyclic Redundancy Check),是數據通信領域常見的差錯校驗碼,特征是信息字段和校驗字段的長度可以任意的選定。
循環冗余檢查(CRC)是一種數據傳輸檢錯功能,對數據進行多項式計算,并將得到的結果附在幀的后面,接受設備也執行類似的算法來保證數據傳輸的正確性和完整性。
二、CRC校驗算法的算法的原理
CRC校驗算法的原理就是:原始幀數據發送之前,在n個bit位的原始數據后面加上通過特定運算得到的K位校驗序列,組成新的幀來發送給接收端。接收端會根據原始數據后的校驗序列再次進行特定的運算,若正確則接受,若結果錯誤則丟棄。
把上面的K位校驗碼序列就稱為:FCS
CRC校驗算法原理的示圖如下:
我們把特定的運算就稱為異或運算。
這樣看來CRC校驗算法也就是把原始數據通過異或運算得到FCS,接收端根據原始數據再次運算如果相等那么就接收。那么CRC算法的核心就是如何得到FCS。
假設要發送的數據是M,M里面有K個數據,現在要計算冗余碼。冗余碼的計算方法如下:
1、用二進制模2運算來進行2^n*M也就是M左移了n位,也即是在M的后面加上了n個0,現在M的長度就是K+n
2、用M去除收發雙方事先商定的長度為n+1的除數p,得到余數是R
3、這個R就是FCS(幀檢驗序列),將這個FCS序列加到M后面發出去就行了。
最后接收端對數據進行CRC校驗,若余數為R就表示這個幀沒有錯,就接受。若R不為0表示這個幀出錯就丟棄。
一般在數據傳輸之前,發送端與接收端會相互約定好一個除數(也是一個二進制序列,用來進行模2算法)。這個除數就是生成多項式。這個多項式的最高位和最低位必須為1。
常見的生成多項式為:
CRC8=X8+X5+X4+1(100110001)
CRC-CCITT=X16+X12+X5+1(1001000000100001)
CRC16=X16+X15+X5+1(11000000000100001)
CRC12=X12+X11+X3+X2+1(1100000001101)
CRC32=X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+1
(100000100110000010001110110011111)
給個栗子吧:
M= 101001,p = 1101,n = 3
M是要發送的數據,p是除數,n是在M后面差錯檢測的n位冗余碼
發送端:
M=(2^n*M),所以M=101001000
用M除以p:
得到的余數是FCS,將其加到M的后面就是要發送的幀
M = 101001000 + FCS = 101001001
接收端:
接收到的每一幀都要進行差錯檢驗,假設收到的101001001,p=1101,具體如下:
我們可以看到最后的余數R=0,沒有出錯,所以信息是被接收的。
三、CRC算法的編程實現
下面我們通過一個栗子來說明是如何實現CRC校驗的,生成多項式為:100110001(簡記0x31),也就是CRC-8
計算步驟如下:
(1) 將CRC寄存器(8-bits,比生成多項式少1bit)賦初值0
(2) 在待傳輸信息流后面加入8個0
(3) While (數據未處理完)
(4) Begin
(5) If (CRC寄存器首位是1)
(6) reg = reg XOR 0x31
(7) CRC寄存器左移一位,讀入一個新的數據于CRC寄存器的0 bit的位置。
(8) End
(9) CRC寄存器就是我們所要求的余數。
程序實現的示圖:
代碼展示:(代碼來自參考文章的鏈接里面)
代碼是C++實現了CRC8、CRC16和CRC32,代碼參考如下:
#ifndef CRCCOMPUTE_H
#define CRCCOMPUTE_H #include <stdint.h> template <typename TYPE> class CRC
{
public:
CRC(); CRC(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value);
void build(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value);
/** * Compute the CRC checksum of a binary message block. * @para message, 用來計算的數據 * @para nBytes, 數據的長度 */ TYPE crcCompute(
char * message, unsigned
int nBytes); TYPE crcCompute(
char * message, unsigned
int nBytes, bool reinit);
protected: TYPE m_polynomial; TYPE m_initial_remainder; TYPE m_final_xor_value; TYPE m_remainder; TYPE crcTable[
256];
int m_width;
int m_topbit;
/** * Initialize the CRC lookup table. * This table is used by crcCompute() to make CRC computation faster. */ void crcInit(
void);
}; template <typename TYPE>
CRC<TYPE>::CRC()
{ m_width =
8 * sizeof(TYPE); m_topbit =
1 << (m_width -
1);
} template <typename TYPE>
CRC<TYPE>::CRC(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value)
{ m_width =
8 * sizeof(TYPE); m_topbit =
1 << (m_width -
1); m_polynomial = polynomial; m_initial_remainder = init_remainder; m_final_xor_value = final_xor_value; crcInit();
} template <typename TYPE>
void CRC<TYPE>::build(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value)
{ m_polynomial = polynomial; m_initial_remainder = init_remainder; m_final_xor_value = final_xor_value; crcInit();
} template <typename TYPE>
TYPE CRC<TYPE>::crcCompute(
char * message, unsigned
int nBytes)
{ unsigned
int offset; unsigned
char byte; TYPE remainder = m_initial_remainder;
for( offset =
0; offset < nBytes; offset++) {
byte = (remainder >> (m_width -
8)) ^ message[offset]; remainder = crcTable[
byte] ^ (remainder <<
8); }
return (remainder ^ m_final_xor_value);
} template <typename TYPE>
TYPE CRC<TYPE>::crcCompute(
char * message, unsigned
int nBytes, bool reinit)
{ unsigned
int offset; unsigned
char byte;
if(reinit) { m_remainder = m_initial_remainder; }
for( offset =
0; offset < nBytes; offset++) {
byte = (m_remainder >> (m_width -
8)) ^ message[offset]; m_remainder = crcTable[
byte] ^ (m_remainder <<
8); }
return (m_remainder ^ m_final_xor_value);
} class CRC8 :
public CRC<uint8_t>
{
public:
enum CRC8_TYPE {eCRC8, eAUTOSAR, eCDMA2000, eDARC, eDVB_S2, eEBU, eAES, eGSM_A, eGSM_B, eI_CODE, eITU, eLTE, eMAXIM, eOPENSAFETY, eROHC, eSAE_J1850, eWCDMA}; CRC8(CRC8_TYPE type); CRC8(uint8_t polynomial, uint8_t init_remainder, uint8_t final_xor_value) :CRC<uint8_t>(polynomial, init_remainder, final_xor_value){}
}; class CRC16 :
public CRC<uint16_t>
{
public:
enum CRC16_TYPE {eCCITT, eKERMIT, eCCITT_FALSE, eIBM, eARC, eLHA, eSPI_FUJITSU, eBUYPASS, eVERIFONE, eUMTS, eCDMA2000, eCMS, eDDS_110, eDECT_R, eDECT_X, eDNP, eEN_13757, eGENIBUS, eEPC, eDARC, eI_CODE, eGSM, eLJ1200, eMAXIM, eMCRF4XX, eOPENSAFETY_A, eOPENSAFETY_B, ePROFIBUS, eIEC_61158_2, eRIELLO, eT10_DIF, eTELEDISK, eTMS37157, eUSB, eCRC_A, eMODBUS, eX_25, eCRC_B, eISO_HDLC, eIBM_SDLC, eXMODEM, eZMODEM, eACORN, eLTE}; CRC16(CRC16_TYPE type); CRC16(uint16_t polynomial, uint16_t init_remainder, uint16_t final_xor_value) :CRC<uint16_t>(polynomial, init_remainder, final_xor_value){}
}; class CRC32 :
public CRC<uint32_t>
{
public:
enum CRC32_TYPE {eADCCP, ePKZIP, eCRC32, eAAL5, eDECT_B, eB_CRC32, eBZIP2, eAUTOSAR, eCRC32C, eCRC32D, eMPEG2, ePOSIX, eCKSUM, eCRC32Q, eJAMCRC, eXFER}; CRC32(CRC32_TYPE type);
}; #endif
#include
"crcCompute.h" template <typename TYPE>
void CRC<TYPE>::crcInit(void)
{ TYPE remainder; TYPE dividend;
int bit;
for(dividend =
0; dividend <
256; dividend++) { remainder = dividend << (m_width -
8);
for(bit =
0; bit <
8; bit++) {
if(remainder & m_topbit) { remainder = (remainder <<
1) ^ m_polynomial; }
else { remainder = remainder <<
1; } } crcTable[dividend] = remainder; }
} CRC8::CRC8(CRC8_TYPE
type)
{
switch (
type) {
case eCRC8: m_polynomial =
0x07; m_initial_remainder =
0x00; m_final_xor_value =
0x00;
break;
case eAUTOSAR: m_polynomial =
0x2f; m_initial_remainder =
0xff; m_final_xor_value =
0xff;
break;
case eCDMA2000: m_polynomial =
0x9b; m_initial_remainder =
0xFF; m_final_xor_value =
0x00;
break;
case eDARC: m_polynomial =
0x39; m_initial_remainder =
0x00; m_final_xor_value =
0x00;
break;
case eDVB_S2: m_polynomial =
0xd5; m_initial_remainder =
0x00; m_final_xor_value =
0x00;
break;
case eEBU:
case eAES: m_polynomial =
0x1d; m_initial_remainder =
0xFF; m_final_xor_value =
0x00;
break;
case eGSM_A: m_polynomial =
0x1d; m_initial_remainder =
0x00; m_final_xor_value =
0x00;
break;
case eGSM_B: m_polynomial =
0x49; m_initial_remainder =
0x00; m_final_xor_value =
0xFF;
break;
case eI_CODE: m_polynomial =
0x1d; m_initial_remainder =
0xFD; m_final_xor_value =
0x00;
break;
case eITU: m_polynomial =
0x07; m_initial_remainder =
0x00; m_final_xor_value =
0x55;
break;
case eLTE: m_polynomial =
0x9b; m_initial_remainder =
0x00; m_final_xor_value =
0x00;
break;
case eMAXIM: m_polynomial =
0x31; m_initial_remainder =
0x00; m_final_xor_value =
0x00;
break;
case eOPENSAFETY: m_polynomial =
0x2f; m_initial_remainder =
0x00; m_final_xor_value =
0x00;
break;
case eROHC: m_polynomial =
0x07; m_initial_remainder =
0xff; m_final_xor_value =
0x00;
break;
case eSAE_J1850: m_polynomial =
0x1d; m_initial_remainder =
0xff; m_final_xor_value =
0xff;
break;
case eWCDMA: m_polynomial =
0x9b; m_initial_remainder =
0x00; m_final_xor_value =
0x00;
break;
default: m_polynomial =
0x07; m_initial_remainder =
0x00; m_final_xor_value =
0x00;
break; } crcInit(); } CRC16::CRC16(CRC16_TYPE
type)
{
switch (
type) {
case eCCITT_FALSE:
case eMCRF4XX: m_polynomial =
0x1021; m_initial_remainder =
0xFFFF; m_final_xor_value =
0x0000;
break;
case eIBM:
case eARC:
case eLHA:
case eBUYPASS:
case eVERIFONE:
case eUMTS: m_polynomial =
0x8005; m_initial_remainder =
0x0000; m_final_xor_value =
0x0000;
break;
case eSPI_FUJITSU: m_polynomial =
0x1021; m_initial_remainder =
0x1d0f; m_final_xor_value =
0x0000;
break;
case eCCITT:
case eKERMIT:
case eXMODEM:
case eZMODEM:
case eACORN:
case eLTE: m_polynomial =
0x1021; m_initial_remainder =
0x0000; m_final_xor_value =
0x0000;
break;
case eCDMA2000: m_polynomial =
0xc867; m_initial_remainder =
0xffff; m_final_xor_value =
0x0000;
break;
case eCMS:
case eMODBUS: m_polynomial =
0x8005; m_initial_remainder =
0xffff; m_final_xor_value =
0x0000;
break;
case eDDS_110: m_polynomial =
0x8005; m_initial_remainder =
0x800d; m_final_xor_value =
0x0000;
break;
case eDECT_R: m_polynomial =
0x0589; m_initial_remainder =
0x0000; m_final_xor_value =
0x0001;
break;
case eDECT_X: m_polynomial =
0x0589; m_initial_remainder =
0x0000; m_final_xor_value =
0x0000;
break;
case eDNP:
case eEN_13757: m_polynomial =
0x3d65; m_initial_remainder =
0x0000; m_final_xor_value =
0xffff;
break;
case eGENIBUS:
case eEPC:
case eDARC:
case eI_CODE:
case eX_25:
case eCRC_B:
case eISO_HDLC:
case eIBM_SDLC: m_polynomial =
0x1021; m_initial_remainder =
0xffff; m_final_xor_value =
0xffff;
break;
case eGSM: m_polynomial =
0x1021; m_initial_remainder =
0x0000; m_final_xor_value =
0xffff;
break;
case eLJ1200: m_polynomial =
0x6f63; m_initial_remainder =
0x0000; m_final_xor_value =
0x0000;
break;
case eMAXIM: m_polynomial =
0x8005; m_initial_remainder =
0x0000; m_final_xor_value =
0xffff;
break;
case eOPENSAFETY_A: m_polynomial =
0x5935; m_initial_remainder =
0x0000; m_final_xor_value =
0x0000;
break;
case eOPENSAFETY_B: m_polynomial =
0x755b; m_initial_remainder =
0x0000; m_final_xor_value =
0x0000;
break;
case ePROFIBUS:
case eIEC_61158_2: m_polynomial =
0x1dcf; m_initial_remainder =
0xffff; m_final_xor_value =
0xffff;
break;
case eRIELLO: m_polynomial =
0x1021; m_initial_remainder =
0xb2aa; m_final_xor_value =
0x0000;
break;
case eT10_DIF: m_polynomial =
0x8bb7; m_initial_remainder =
0x0000; m_final_xor_value =
0x0000;
break;
case eTELEDISK: m_polynomial =
0xa097; m_initial_remainder =
0x0000; m_final_xor_value =
0x0000;
break;
case eTMS37157: m_polynomial =
0x1021; m_initial_remainder =
0x89ec; m_final_xor_value =
0x0000;
break;
case eUSB: m_polynomial =
0x8005; m_initial_remainder =
0xffff; m_final_xor_value =
0xffff;
break;
case eCRC_A: m_polynomial =
0x1021; m_initial_remainder =
0xc6c6; m_final_xor_value =
0x0000;
break;
default: m_polynomial =
0x8005; m_initial_remainder =
0x0000; m_final_xor_value =
0x0000;
break; } crcInit();
} CRC32::CRC32(CRC32_TYPE
type)
{
switch (
type) {
case eADCCP:
case ePKZIP:
case eCRC32:
case eBZIP2:
case eAAL5:
case eDECT_B:
case eB_CRC32: m_polynomial =
0x04c11db7; m_initial_remainder =
0xFFFFFFFF; m_final_xor_value =
0xFFFFFFFF;
break;
case eAUTOSAR: m_polynomial =
0xf4acfb13; m_initial_remainder =
0xFFFFFFFF; m_final_xor_value =
0xFFFFFFFF;
break;
case eCRC32C: m_polynomial =
0x1edc6f41; m_initial_remainder =
0xFFFFFFFF; m_final_xor_value =
0xFFFFFFFF;
break;
case eCRC32D: m_polynomial =
0xa833982b; m_initial_remainder =
0xFFFFFFFF; m_final_xor_value =
0xFFFFFFFF;
break;
case eMPEG2:
case eJAMCRC: m_polynomial =
0x04c11db7; m_initial_remainder =
0xFFFFFFFF; m_final_xor_value =
0x00000000;
break;
case ePOSIX:
case eCKSUM: m_polynomial =
0x04c11db7; m_initial_remainder =
0x00000000; m_final_xor_value =
0xFFFFFFFF;
break;
case eCRC32Q: m_polynomial =
0x814141ab; m_initial_remainder =
0x00000000; m_final_xor_value =
0x00000000;
break;
case eXFER: m_polynomial =
0x000000af; m_initial_remainder =
0x00000000; m_final_xor_value =
0x00000000;
break;
default: m_polynomial =
0x04C11DB7; m_initial_remainder =
0xFFFFFFFF; m_final_xor_value =
0xFFFFFFFF;
break; } crcInit();
}
#include <iostream>
#include <stdio.h>
#include "crcCompute.h" using namespace std;
int main(
int argc,
char *argv[])
{ CRC16 crc16(CRC16::eCCITT_FALSE);
char data1[] = {
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9'};
char data2[] = {
'5',
'6',
'7',
'8',
'9'};
unsigned short c1, c2; c1 = crc16.crcCompute(data1,
9); c2 = crc16.crcCompute(data1,
4,
true); c2 = crc16.crcCompute(data2,
5,
false);
printf(
"%04x\n", c1);
printf(
"%04x\n", c2);
return 0;
}
參考文章:
http://blog.csdn.net/liyuanbhu/article/details/7882789
總結
以上是生活随笔為你收集整理的CRC校验算法的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。