基本概念
CRC全称是循环冗余校验(Cyclic Redundancy Check)。
在数据传输过程中,无论传输系统的设计再怎么完美,差错总会存在,这种差错可能会导致在链路上传输的一个或者多个帧被破坏(出现比特差错,0变为1,或者1变为0),从而接受方接收到错误的数据。为尽量提高接受方收到数据的正确率,在接收方接收数据之前需要对数据进行差错检测,当且仅当检测的结果为正确时接收方才真正收下数据。
CRC是一种用于校验通信链路上数字传输准确性的计算方法(通过某种数学运算来建立数据位和校验位的约定关系)。发送方计算机使用某公式计算出被传送数据所含信息的一个值,并将此值附在被传送数据后,接收方计算机则对同一数据进行相同的计算,应该得到相同的结果。如果这两个CRC结果不一致,则说明发送中出现了差错,接收方计算机可要求发送方计算机重新发送该数据。
CRC是一种数据错误检查技术,是一种常用的检错码,但并不能用于自动纠错。
多项式
了解了CRC的基本概念,那么接下来就要知道多项式的概念了。
以这个IEEE802.3标准CRC32多项式为例:x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x+ 1
x32则对应32bit = 1, x26则对应26bit=1,得出一个值:(1<<32)|(1<<26)|(1<<23)|(1<<22)|…|(1<<1)|(1)=0x104C11DB7,对于CRC32取低32位,则=0x4C11DB7
一般是用这个值通过一定方法生成长度为256的码表,对于CRC32,表内每个元素都为32bit。然后再用一定的方法查表最后得出CRC32值。
为什么用查表这种方法呢?因为,世界上一共就256个字符,每装载一个就运算一遍,很浪费CPU资源,不如直接把每个字符的CRC都算出来存入数组。因此,就有了CRC编码字符表。
接下来我们看下最终实现代码:
# 定义一个256个元素的全0数组
custom_crc32_table = [0 for x in range(0,256)]
def generate_crc32_table():
for i in range(256):
c = i << 24
for j in range(8):
if (c & 0x80000000):
c = (c << 1) ^ 0x04C11DB7
else:
c = c << 1
custom_crc32_table[i] = c & 0xffffffff
def getCrc32(bytes_arr):
length = len(bytes_arr)
if bytes_arr != None:
crc = 0xffffffff
for i in range(0, length):
crc = (crc << 8) ^ custom_crc32_table[(getReverse(bytes_arr[i], 8) ^ (crc >> 24)) & 0xff]
else:
crc = 0xffffffff
# - 返回计算的CRC值
crc = getReverse(crc ^ 0xffffffff, 32)
return crc
def getReverse(tempData, byte_length):
reverseData = 0
for i in range(0, byte_length):
reverseData += ((tempData>>i)&1)<<(byte_length-1-i)
return reverseData
我们再来看看使用反转后的多项式(0xEDB88320)代码实现:
# 定义一个256个元素的全0数组
reversal_crc32_table = [0 for x in range(0,256)]
def reversal_init_crc32_table():
for i in range(256):
c = i
for j in range(8):
if (c & 0x00000001):
c = (c >> 1) ^ 0xEDB88320
else:
c = c >> 1
reversal_crc32_table[i] = c & 0xffffffff
def reversal_getCrc32(bytes_arr):
length = len(bytes_arr)
if bytes_arr != None:
crc = 0xffffffff
for i in range(0, length):
crc = (crc >> 8) ^ reversal_crc32_table[ (bytes_arr[i] ^ crc) & 0xff ]
else:
crc = 0xffffffff
crc = crc ^ 0xffffffff
return crc
测试代码:
if __name__ == "__main__":
import struct
import zlib
import binascii
s = struct.pack('>i', 400)
print('当前CRC输入初始值:', (s, type(s)))
test = binascii.crc32(s) & 0xffffffff
print('算出来的CRC值:', '0x'+"{:0>8s}".format(str('%x'%test)))
test = zlib.crc32(s) & 0xffffffff
print('算出来的CRC值:', '0x'+"{:0>8s}".format(str('%x'%test)))
buf_s = [0x00, 0x00, 0x01, 0x90]
generate_crc32_table()
crc_stm = getCrc32(bytearray(buf_s)) & 0xffffffff
print('算出来的CRC值:', '0x' + "{:0>8s}".format(str('%x' % crc_stm)))
reversal_init_crc32_table()
crc_stm = reversal_getCrc32(bytearray(buf_s)) & 0xffffffff
print('反转算出来的CRC值:', '0x' + "{:0>8s}".format(str('%x' % crc_stm)))
再看看最终的运行结果:
当前CRC输入初始值: (b'\x00\x00\x01\x90', <class 'bytes'>)
算出来的CRC值: 0xc8507d19
算出来的CRC值: 0xc8507d19
算出来的CRC值: 0xc8507d19
反转算出来的CRC值: 0xc8507d19