最近客户有CRC校验的需求,即希望Android端对字符串进行校验,并将生成的2个字节的CRC校验码追加到字符串中,然后一起写入文件。当系统联网后,会通过写入的字符串去向服务器请求设备序列号,服务器会根据该字符串的原始字符串(不含校验码)进行校验,如果校验出来的值跟最后两个字节的校验码匹配上,则下发一个序列号,否则为空。
CRC的校验方式多种多样,每个客户都有自己的校验方式。此次客户提供了C语言版本的校验方式,这边要将C语言版本的校验转化为Java版本。
但由于C语言和Java的基本类型对不上,如C语言的变量类型有符号之分,而Java则统一都带符号,并没有所谓的无符号类型变量。若未接触过的同学可能要费点时间去做转化,这里给出转化的技巧。
这里先上C语言版本的校验,代码如下:
unsigned short int CRC_TABLE[16]={0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7, 0x8108,0x9129,0xA14A,0xB16B,0xC18C,0xD1AD,0xE1CE,0xF1EF}; unsigned int getCRC( char *pbuffer, unsigned int buff_len) { unsigned short int mCRC; unsigned char mTemp; mCRC=0x0; while(buff_len--) { mTemp=(unsigned char)(mCRC>>0x0C); mCRC<<=4; mCRC^=CRC_TABLE[mTemp^((*pbuffer)>>0x04)]; mTemp=(unsigned char)(mCRC>>0x0C); mCRC<<=4; mCRC^=CRC_TABLE[mTemp^((*pbuffer)&0x0F)]; pbuffer++; } return (unsigned int)mCRC; } 复制代码
一般CRC校验都会有一个码表,用于生成校验码。
由于C语言有有符号和无符号之分,而Java则没有这种区分,那么在转化的时候,要注意符号的问题。这里有个技巧,转化时,C语言和Java的类型对应关系如下:
int->int short->int char*->String/byte[] char->short 复制代码
什么意思?即C语言使用int类型的变量时,Java也使用int类型变量,C语言使用short类型变量时,Java使用int类型变量。。。
贴上Java的CRC校验代码,大家对照着看就明白:
private byte[] getCRC16(String data) { int crc = 0x0; //这里只需用到int的低16位即可 short tem = 0x00; char[] ch = data.toCharArray(); for (char c : ch) { byte b = (byte)c; tem = (short)((crc >> 0x0c) & 0x00ff); crc <<= 4; crc &= 0x0000ffff; //保证高16位一直为0 crc ^=crc16tab[(tem ^ (b >> 0x04))&0x000000ff]; //获取高八位CRC校验码 crc &= 0x0000ffff; tem = (short)((crc >> 0x0c) & 0x00ff); crc <<= 4; crc &= 0x0000ffff; crc ^= crc16tab[(tem ^ (b & 0x0f)) & 0x000000ff]; //获取低八位CRC校验码 crc &= 0x0000ffff;//只需低16位的CRC校验码 } byte[] res = intToByteArray(crc);//将int类型转为byte数组 return res; } 复制代码
需要注意的是,在CRC校验中,有针对16进制字节进行校验的,有针对字符进行校验的,比如需要校验如下内容:
0001020304050607 复制代码
那么这时需要确认清楚,到底是校验字节,还是逐个字符的进行校验。本文则是逐个字符进行校验。
CRC校验多见于通讯协议中,会对通讯内容进行校验,然后将生成的校验码追加到通讯帧中。也有像本文这样子的,对某个文件中的内容进行校验,然后生成校验码,提高安全性。