循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存后可能出现的错误。 它是利用除法及余数的原理来作错误侦测的。 CRC校验计算速度快,检错能力强,易于用编码器等硬件电路实现。
- 模2除法:也叫模2运算,就是结果除以2后取余数。模2除法每一位除的结果不影响其它位,即不向上一位借位,所以实际上就是异或。在CRC计算中有应用到模2除法。
- 多项式与二进制:二进制可表示成多项式的形式,比如二进制1101表示为: x^3+x^2+x^0。
CRC校验本质上是选取一个合适的除数,也就是生成多项式,要进行校验的数据是被除数,然后做模2除法,得到的余数就是CRC校验值
发送端的处理
- 在原始数据bit串后补充32位bit0,得到新的bit串;
- 新的bit串以生成多项式为除数进行模2除法,得到的余数为CRC32校验码;
- 将校验码替换原来的32位bit0,结果作为发送数据发送出去
接收端的处理
接收端对收到的数据以同一个生成多项式进行模2除法,校验余数是否为0,为0则认为数据传输无差错(检测并非百分百准确)。
/* crc32.c from https://github.com/lammertb/libcrc/blob/v2.0/src/crc32.c */
#include<stdib.h>
// G(x) = 1+x+x^2+x^4+x^5+^7+x^8+x^10+x^11+x^12+x^16+x^22+x^23+x^26(+x^32)
// 11101101101110001000001100100000(1)
#define CRC_POLY_32 0xEDB88320L
#define CRC_START_32 0xFFFFFFFFL
static int crc_tab32_init = 0;
static uint32_t crc_tab32[256];
static void init_crc32_tab();
uint32_t crc_32(const unsigned char *input_str, size_t num_bytes);
uint32_t crc_32(const unsigned char *input_str, size_t num_bytes)
{
uint32_t crc;
uint32_t tmp;
uint32_t long_c;
const unsigned char *ptr;
size_t a;
if (!crc_tab32_init)
init_crc32_tab();
crc = CRC_START_32;
ptr = input_str;
if ( ptr != NULL )
for (a=0; a<num_bytes; a++)
{
long_c = 0x000000FFL & (uint32_t) *ptr;
tmp = crc ^ long_c;
crc = (crc >> 8) ^ crc_tab32[ tmp & 0xFF ];
ptr++;
}
crc ^= 0xFFFFFFFFL;
return crc & 0xFFFFFFFFL;
} /* crc_32 */
uint32_t update_crc_32(uint32_t crc, unsigned char c)
{
uint32_t tmp;
uint32_t long_c;
long_c = 0x000000FFL & (uint32_t) c;
if ( ! crc_tab32_init )
init_crc32_tab();
tmp = crc ^ long_c;
crc = (crc >> 8) ^ crc_tab32[ tmp & 0xff ];
return crc & 0xFFFFFFFFL;
} /* update_crc_32 */
static void init_crc32_tab()
{
uint32_t i;
uint32_t j;
uint32_t crc;
for (i=0; i<256; i++)
{
crc = i;
for (j=0; j<8; j++)
{
if ( crc & 0x00000001L )
crc = ( crc >> 1 ) ^ CRC_POLY_32;
else
crc = crc >> 1;
}
crc_tab32[i] = crc;
}
crc_tab32_init = 1;
} /* init_crc32_tab */