循环冗余校验(CRC)是一种根据网络数据封包或电脑档案等数据产生简短固定位数的一种散列函数,主要用来检测或校验数据传输或者保存后可能出现的错误。它是由W. Wesley Peterson在他1961年发表的论文中披露。[来自维基百科]
CRC校验的基本思想是利用线性编码理论,在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的监督码(既CRC码) r位,并附在信息后边,构成一个新的二进制码序列数共(k+r)位,最后发送出去。在接收端, 则根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。
假设要生成一个16位的CRC码,它产生的规则是先将要发送的二进制序列数左移16位(既乘以216)后,再除以一个多项式,最后所得到的余数既是CRC码。任意一个由二进制位串组成的代码都可以和一个系数仅为‘0’ 和‘1’取值的多项式一一对应。例如:代码1010111对应的多项式为x6+x4+x2+x+1,而多项式为 x5+x3+x2+x+1对应的代码101111。
Boost里的CRC库
Boost中的CRC库定义于头文件<boost/crc.hpp>中,主要提供了两个CRC类:class crc_basic和class crc_optimal。crc_basic 是按位运算的,速度较慢。crc_optimal按位数取相应的字节数作为单位运算,速度很快。
crc_basic和crc_optimal的用法相差不大,主要区别是CRC参数设定的位置不同,下面是它们的类声明和构造函数
template < std::size_t Bits > class crc_basic; explicit crc_basic( value_type truncated_polynominal, value_type initial_remainder = 0, value_type final_xor_value = 0, bool reflect_input = false, bool reflect_remainder = false ); template < std::size_t Bits, impl_def TruncPoly = 0u, impl_def InitRem = 0u, impl_def FinalXor = 0u, bool ReflectIn = false, bool ReflectRem = false > class crc_optimal; explicit crc_optimal( value_type init_rem = InitRem );
对比crc_basic构造函数参数名和crc_optimal模板参数名,可以看出其实它们是一样的。只是crc_basic在构造函数里设置CRC参数,而crc_optimal是在模板参数里。
下面先说说这几个参数的意义:
Bits | CRC校验位数 |
truncated_polynominal/TruncPoly | 多项式,由于除数总是比余数大一位,而作为除数的多项式最高位总是为1。在描述CRC多项式时,最高位被忽略,这样可以让多项式(除数)和CRC校验和(余数)使用同一数据类型来表示。 |
initial_remainder/InitRem | 初始值 |
final_xor_value/FinalXor | 最后的异或值,返回值是校验和与之异或的值。 |
reflect_input/ReflectIn | 反转输入值 |
reflect_remainder/ReflectRem | 反转输出值 |
几个常用方法
操作 | 描述 |
---|---|
void process_bit( bool bit ); |
把一个位(bit)作为数据传递给 CRC 机,更新中间 CRC。只在crc_basic中定义。 |
void process_bits( unsigned char bits, std::size_t bit_count ); |
对 bits 中最低的 bit_count 个位(从最重要的位算起)应用 process_bit。如果 bit_count 超过了每个字节的位数,结果未定义。只在 crc_basic中定义。 |
void process_byte( unsigned char byte ); |
对 byte 中的所有位应用 process_bit 。如果不需要反射,这些位以从最重要到最不重要的顺序依次提供;否则以相反的顺序提供。 |
void process_block( void const *bytes_begin, void const *bytes_end ); |
对给定的从 bytes_begin 开始,到 bytes_end 结束的内存块应用 process_byte。各个字节依上述顺序处理。 |
void process_bytes( void const *buffer, std::size_t byte_count ); |
对给定的从 buffer 开始的长度为 byte_count 字节的内存块应用 process_byte 。各个字节依升序处理。 |
value_type checksum() const; |
返回到目前为止所有传递过去的数据的 CRC 检验和,这个值可能经过了余数反射和异或运算。 |
void operator ()( unsigned char byte ); |
调用 process_byte 。有了这个方法,就可以把 CRC 机对象当作一个(有状态的)函数对象来使用。只在快速 CRC 机中定义。 |
value_type operator ()() const; |
调用 checksum 。有了这个这个方法,就可以把 CRC 机对象当作一个发生器(generator)函数对象来使用。只在快速 CRC 机中定义。 |
示例代码
#include <boost/crc.hpp> // for boost::crc_basic, boost::crc_optimal #include <boost/cstdint.hpp> // for boost::uint16_t #include <algorithm> // for std::for_each #include <cassert> // for assert #include <cstddef> // for std::size_t #include <iostream> // for std::cout #include <ostream> // for std::endl // Main function int main () { // This is "123456789" in ASCII unsigned char const data[] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }; std::size_t const data_len = sizeof( data ) / sizeof( data[0] ); // The expected CRC for the given data boost::uint16_t const expected = 0x29B1; // Simulate CRC-CCITT boost::crc_basic<16> crc_ccitt1( 0x1021, 0xFFFF, 0, false, false ); crc_ccitt1.process_bytes( data, data_len ); assert( crc_ccitt1.checksum() == expected ); // Repeat with the optimal version (assuming a 16-bit type exists) boost::crc_optimal<16, 0x1021, 0xFFFF, 0, false, false> crc_ccitt2; crc_ccitt2 = std::for_each( data, data + data_len, crc_ccitt2 ); assert( crc_ccitt2() == expected ); std::cout << "All tests passed." << std::endl; return 0; }
另外,Boost的CRC库已经定义了几个常用的CRC参数组合:
typedef crc_optimal<16, 0x8005, 0, 0, true, true> crc_16_type; typedef crc_optimal<16, 0x1021, 0xFFFF, 0, false, false> crc_ccitt_type; typedef crc_optimal<16, 0x8408, 0, 0, true, true> crc_xmodem_type; typedef crc_optimal<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc_32_type;
附常用生成多项式
下面是常用 CRC(按照 ITU-IEEE 规范)[来自维基百科]
名称 | 多项式 | 表示法:正常或者翻转 |
---|---|---|
CRC-1 | x + 1 (用途:硬件,也称为奇偶校验位) |
0x1 or 0x1 (0x1) |
CRC-5-CCITT | x5 + x3 + x + 1 (ITU G.704 标准) | 0x15 (0x??) |
CRC-5-USB | x5 + x2 + 1 (用途:USB 信令包) | 0x05 or 0x14 (0x9) |
CRC-7 | x7 + x3 + 1 (用途:通信系统) | 0x09 or 0x48 (0x11) |
CRC-8-ATM | x8 + x2 + x + 1 (用途:ATM HEC) | 0x07 or 0xE0 (0xC1) |
CRC-8-CCITT | x8 + x7 + x3 + x2 + 1 (用途:1-Wire 总线) | |
CRC-8-Dallas/Maxim | x8 + x5 + x4 + 1 (用途:1-Wire bus) | 0x31 or 0x8C |
CRC-8 | x8 + x7 + x6 + x4 + x2 + 1 | 0xEA(0x??) |
CRC-10 | x10 + x9 + x5 + x4 + x + 1 | 0x233 (0x????) |
CRC-12 | x12 + x11 + x3 + x2 + x + 1 (用途:通信系统) |
0x80F or 0xF01 (0xE03) |
CRC-16-Fletcher | 参见 Fletcher's checksum | 用于 Adler-32 A & B CRC |
CRC-16-CCITT | x16 + x12 + x5 + 1 (X25, V.41, Bluetooth, PPP, IrDA) | 0x1021 or 0x8408 (0x0811) |
CRC-16-IBM | x16 +x15 + x2 + 1 | 0x8005 or 0xA001 (0x4003) |
CRC-16-BBS | x16 + x15 + x10 + x3 (用途:XMODEM 协议) | 0x8408 (0x????) |
CRC-32-Adler | See Adler-32 | 参见 Adler-32 |
CRC-32-MPEG2 | See IEEE 802.3 | 参见 IEEE 802.3 |
CRC-32-IEEE 802.3 | x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x+ 1 | 0x04C11DB7 or 0xEDB88320 (0xDB710641) |
CRC-32C (Castagnoli) | x32 + x28 + x27 + x26 + x25 + x23 + x22 + x20 + x19 + x18 + x14 + x13 +x11 + x10 + x9 + x8 + x6 + 1 | 0x1EDC6F41 or 0x82F63B78 (0x05EC76F1) |
CRC-64-ISO | x64 + x4 + x3 + x + 1 (use: ISO 3309) |
0x000000000000001B or 0xD800000000000000 (0xB000000000000001) |
CRC-64-ECMA-182 | x64 + x62 + x57 + x55 + x54 + x53 + x52 + x47 + x46 + x45 + x40 + x39 +x38 + x37 + x35 + x33 + x32 + x31 + x29 + x27 + x24 + x23 + x22 + x21 + x19 + x17 + x13 + x12 + x10 +x9 + x7 + x4 + x + 1 (as described in ECMA-182 p.63) |
0x42F0E1EBA9EA3693 or 0xC96C5795D7870F42 (0x92D8AF2BAF0E1E85) |
CRC-128 | IEEE-ITU 标准。被 MD5 & SHA-1 取代 | |
CRC-160 | IEEE-ITU 标准。被 MD5 & SHA-1 取代 |