CRC是通信领域中用于校验数据传输正确性的最常用机制,也是Hash算法的一个典型应用,Hash一般翻译为“散列”,也可直接音译为“哈希”,就是把任意长度的输入(又叫做预映射,pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是散列值的空间通常远小于输入空间,不同的输入可能会散列成相同的输出,而不可能从散列值唯一的确定输入值。
关于CRC校验:(16位二进制数),CRC码由发送设备计算,放置于所发送信息帧的尾部。接收信息的设备重新计算所接收信息(除RCR部分)的CRC,比较计算得到的
CRC是否与接收到的CRC相符,如果两者不相符,则认为数据传输出错,如果相符说明数据传输正确。
(1)预1个16位的寄存器为0xFFFF,称此寄存器为CRC寄存器
(2)把分i一个8位二进制数据(即通信信息帧的第一个字节)与16位的CRC寄存器的低8位相异或,把结果存放于CRC寄存器
(3)把CRC寄存器的内容右移一位,用0填补最高位,并检查右移后的移出位。
(4)如果移出位为0,重复(3),如果移出位为1,CRC寄存器与0xA001进行异或
(5)重复(3)(4),直到右移8次,这样整个8位数据就全部进行了处理
(6)重复(2)到(5),进行通信信息帧下一个字节的处理
(7)将该通信信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低位已经交换
(8)最后得到的CRC寄存器内容即为CRC码
程序代码如下:
#include <stdio.h> unsigned short modbus_crc(unsigned char* p,int len) { unsigned short ret=0xFFFF; int i=0; int k=0; for(i=0;i<len;++i){ ret^=p[i]; for(k=0;k<8;++k){ ret=(ret&0x01)?((ret>>1)^0xA001):(ret>>1); } } ret=((ret&0xFFFF)<<8) | ((ret&0xFFFF)>>8); return ret; } int main() { unsigned char input[]={0x01,0x04,0x0D,0x28,0x00,0x02,0x01,0x02,0x00,0x53,0x00,0x01}; unsigned short out=0; int i=0; int len=sizeof(input); out=modbus_crc(input,sizeof(input)); printf("Input:"); for(i=0;i<len;i++) { printf(" 0x%.2X",input[i]); } printf("\nModbus CRC is: 0x%.4X\n",out); return 0; }
常见的 Hash 算法有:MAC, CRC, MD5/MD4, SHA 等。
PHP版本
/** * 16位CRC散列算法(返回 十进制 的字符串形式) * * @param $string * @return int */ public function php_crc16($string) { $crc = 0xFFFF; for ($x = 0; $x < strlen($string); $x++) { $crc = $crc ^ ord($string[$x]); for ($y = 0; $y < 8; $y++) { if (($crc & 0x0001) == 0x0001) { $crc = (($crc >> 1) ^ 0xA001); } else { $crc = $crc >> 1; } } } return $crc; }
举例:
$crc = php_crc16("我是 温建宝"); // 十进制 字符串 $bin = decbin($crc); // 二进制 字符串 $hex = dechex($crc); // 16进制 字符串 echo '十进制:', $crc, "\r\n"; echo '二进制:' . $bin, "\r\n"; echo '16进制:' . $hex, "\r\n";
输出:
十进制:56480 二进制:1101110010100000 16进制:dca0