一、什么是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, TYP