赞
踩
循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存后可能出现的错误。它是利用除法及余数的原理来作错误侦测的。
项目中涉及到OTA 蓝牙升级模块,由于BLE 每次发包只能发20字节,多余的需要自己分包处理。我们的上传的文件有100k
所以 CRC使用非常有必要。
SOH | 包序号 | 序号补码 | 数据区 | CRC |
Byte 1 | Byte 2 | Byte 3 | Byte 4-18 | Byte 19-20 |
客户端与服务端的通信:
通讯过程
发送OTA字符:
下面是发送OTA 的代码
- /**
- * 开始联机 OTA 初始化 获取文件信息属性 更新progress.
- */
- private void updateUIandSendOTAcomend() {
- try {
- mInputStream = getActivity().getResources().getAssets().open(filePath == null ? Constance.odd_filename : filePath);
- sendInputStream = getActivity().getResources().getAssets().open(filePath == null ? Constance.odd_filename : filePath);
- mLength = mInputStream.available();
- byte[] totle_bins = readBytes(mInputStream);
- mPb_update.setMax(mLength);
- String header_ota = GattBluetoothType.OTA.BLE_RESPOND_OTA;
- mBytes.clear();
- mBytes.add(header_ota.getBytes()); //OTA 字节包
- int filesize = mLength;
- Log.i(TAG, "updateUIandSendOTAcomend: totlesize=" + mLength);
- byte[] filesize_byte = intToByteArray1(filesize);
- mBytes.add(filesize_byte); //文件大小
- int filesize_contrary = ~(filesize);
- byte[] filesize_complement = intToByteArray1(filesize_contrary);
- mBytes.add(filesize_complement); //文件大小反码
- byte[] crc16_rerify = CodecUtil.crc16Bytes(totle_bins); //crc
- byte[] crc16_complement = {(byte) ~crc16_rerify[0], (byte) ~crc16_rerify[1]};
- mBytes.add(crc16_rerify); //CRC
- mBytes.add(crc16_complement); //CRC 反码
-
- mHead_content_bytes = sysCopy(mBytes);
- mPb_update.setProgress(0);
- writeData(mHead_content_bytes); //写入BLE
-
- int size = mLength / 1024;
- tvAPkSize.setText("apk :" + size + "k");
- tvProgress.setText("0%");
- Log.i(TAG, "updateUIandSendOTAcomend: ");
- /* for (byte[] aByte : mBytes) {
- Log.i(TAG, "updateUIandSendOTAcomend: 长度="+aByte.length+" tostring= "+DataSwitchUtils.bytesToHexString(aByte));
- }*/
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
发送文件:
由于包头包尾 已经占据了5个字节(包头01,包序号,包序号反码,crc 2个字节),所以每次发包内容只是从文件中读取15个字节,100k 计算估计1万次 10分钟左右。(后面再说优化方案4分钟)
bytenum 是15 ,for循环是检测长度不够15的最后一包,没有内容字节数组做0xff处理。例如最后一包只剩下06一个字节了,那15的数组里面的内容就是06 FF FF FF FF FF FF FF FF FF FF FF FF FF FF。如果包序号是00 反码就是11,比如CRC 计算结果为C507
最后一包:01 00 11 06 FF FF FF FF FF FF FF FF FF FF FF FF FF FF C5 07
下面是发送文件的方法
- private boolean setUpdatePackageACK(boolean interruptUPGRADE) {
- try {
-
- byte[] send_package = new byte[byteNums];
- int n = sendInputStream.read(send_package, 0, byteNums);
- if (n > 0) {
- if ((mLength - counter) < byteNums) { //最后一包 空至0XFF
- int num = mLength - counter;
- for (int i = 0; i < send_package.length; i++) {
- if (i >= num) {
- send_package[i] = (byte) 0xff;
- }
- }
- }
- data = send_package.length;
- byte[] header_byte = {0x01};
- byte[] order = null;
- if (interruptUPGRADE) {
- order = new byte[]{order_index--};
- } else {
- order = new byte[]{order_index++};
- }
- Log.d(TAG, "--------------order_index = " + order_index);
- byte[] complement = {(byte) ~order[0]};
- byte[] crc16_rerify = CodecUtil.crc16Bytes(send_package);
- mBytes.clear();
- mBytes.add(header_byte);
- mBytes.add(order);
- mBytes.add(complement);
- mBytes.add(send_package);
- mBytes.add(crc16_rerify);
- content_bytes = null;
- content_bytes = sysCopy(mBytes);
- writeData(content_bytes);
- mPb_update.setProgress(counter);
- Log.i(TAG, "setUpdatePackageACK: ");
- } else {
- Log.i(TAG, "setUpdatePackageACK: false inputstream");
- }
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- return false;
- }
后面贴出发送过程中涉及到的方法代码:
-
- public static byte[] sysCopy(List<byte[]> srcArrays) {
- int len = 0;
- for (byte[] srcArray : srcArrays) {
- len += srcArray.length;
- }
- byte[] destArray = new byte[len];
- int destLen = 0;
- for (byte[] srcArray : srcArrays) {
- System.arraycopy(srcArray, 0, destArray, destLen, srcArray.length);
- destLen += srcArray.length;
- }
- return destArray;
- }
-
- // int 转字节
- public static byte[] intToByteArray1(int i) {
- byte[] result = new byte[4];
- result[0] = (byte) ((i >> 24) & 0xFF);
- result[1] = (byte) ((i >> 16) & 0xFF);
- result[2] = (byte) ((i >> 8) & 0xFF);
- result[3] = (byte) (i & 0xFF);
- return result;
- }
-
- // 流转换为字节数组的方法
- public byte[] readBytes(InputStream in) throws IOException {
- byte[] temp = new byte[in.available()];
- byte[] result = new byte[0];
- int size = 0;
- while ((size = in.read(temp)) != -1) {
- byte[] readBytes = new byte[size];
- System.arraycopy(temp, 0, readBytes, 0, size);
- result = Tools.mergeArray(result, readBytes);
- }
- return result;
- }
后面贴出的事 CRC16 计算代码
- package com.rrioo.sateliteone.util;
- /**
- * @version :2015年1月28日 下午2:03:54
- *
- * 类说明 :byte\short\crc转换
- */
- public class CodecUtil {
-
- static CRC16 crc16 = new CRC16();
- private CodecUtil() {
- }
- public static byte[] short2bytes(short s) {
- byte[] bytes = new byte[2];
- for (int i = 1; i >= 0; i--) {
- bytes[i] = (byte)(s % 256);
- s >>= 8;
- }
- return bytes;
- }
- public static short bytes2short(byte[] bytes) {
- short s = (short)(bytes[1] & 0xFF);
- s |= (bytes[0] << 8) & 0xFF00;
- return s;
- }
- /*
- * 获取crc校验的byte形式
- */
- public static byte[] crc16Bytes(byte[] data) {
- return short2bytes(crc16Short(data));
- }
- /*
- * 获取crc校验的short形式
- */
- public static short crc16Short(byte[] data) {
- return crc16.getCrc(data);
- }
- }
- package com.rrioo.sateliteone.util;
-
- /**
- * @version :2015年1月28日 下午2:03:54
- *
- * 类说明 :获取crc校验码
- */
- public class CRC16 {
-
- private short[] crcTable = new short[256];
- private int gPloy = 0x1021; // 生成多项式
- public CRC16() {
- computeCrcTable();
- }
- private short getCrcOfByte(int aByte) {
- int value = aByte << 8;
- for (int count = 7; count >= 0; count--) {
- if ((value & 0x8000) != 0) { // 高第16位为1,可以按位异或
- value = (value << 1) ^ gPloy;
- } else {
- value = value << 1; // 首位为0,左移
- }
- }
- value = value & 0xFFFF; // 取低16位的值
- return (short)value;
- }
- /*
- * 生成0 - 255对应的CRC16校验码
- */
- private void computeCrcTable() {
- for (int i = 0; i < 256; i++) {
- crcTable[i] = getCrcOfByte(i);
- }
- }
- public short getCrc(byte[] data) {
- int crc = 0;
- int length = data.length;
- for (int i = 0; i < length; i++) {
- crc = ((crc & 0xFF) << 8) ^ crcTable[(((crc & 0xFF00) >> 8) ^ data[i]) & 0xFF];
- }
- crc = crc & 0xFFFF;
- return (short)crc;
- }
- }
优化方案:
BLE空中传输20字节是不可变的,致使你打开了最大传输字节的MTU 也就是23个字节的样子。
我的做法是 减少ACK 的校验。没发送8个20 字节包后再 ACK 校验一次 ,即一次可以发送文件内容为155个字节。服务端做好丢包检测就好,一旦发现1S之内没有收到客户端的包 就扔掉所有数据。
列如:客户端发送160个字节 结果有20 字节丢包了,服务端只检测到140个字节,并不反回ACK ,移动端检测ACk 超时重发2秒之后。此时服务端已经扔掉了不完整的包,清楚buf 继续等待下一次160字节的到来。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。