粘包解决方法
方法1
getXOR——是校验方法
/** * 最小数据包的长度(除开数据的N个字节) * 帧头 保留字节 协议控制字 地址字段 命令长度 命令码 命令数据 校验和 * 2字节 3字节 1字节 2或8字节 2字节 2字节 0-1100字节 2字节 * SYN RES PTROL ADDR SLEN COMMAND APPDATA CRC16 */ int MIN_PACK_LEN = 2 + 3 + 1 + 2 + 2 + 2 + 2;
/** * 数据:0-N字节,N小于等于250 */ int SLEN = 1100; int MAX_DATA_N = 255;
private final ByteBuffer mByteBuffer; public SerialReadThread(InputStream is) { mInputStream = new BufferedInputStream(is); mByteBuffer = ByteBuffer.allocate(1024); mByteBuffer.clear(); }
private boolean isValid(byte[] recvCheck, byte[] myCheck) { //LgqLogutil.d("rr=== "+ MyByteUtil.bytes2HexStr(recvCheck)+",,,m==="+MyByteUtil.bytes2HexStr(myCheck)); for (int i = 0, n = recvCheck.length; i < n; ++i) { if (recvCheck[i] != myCheck[i]) { return false; } } return true; }
/** * 处理获取到的数据 * * @param received * @param size */ private void onDataReceive(byte[] received, int size) { // TODO: 2018/3/22 解决粘包、分包等 try { //LogPlus.e("Receiver", "接收数据=" + ByteUtil.bytes2HexStr(bytes, offset, length)); byte b; int readable; mByteBuffer.put(received, 0, size); mByteBuffer.flip(); out: while ((readable = mByteBuffer.remaining()) >= Protocol.MIN_PACK_LEN) { /*只处理数据帧, 不处理响应帧*/ mByteBuffer.mark(); // 标记一下开始的位置 int frameStart = mByteBuffer.position(); byte[] frameHead = Protocol.FRAME_HEAD; for (byte h : frameHead) { b = mByteBuffer.get(); if (b != h) { // 不满足帧头,就跳到第二位,重新开始 mByteBuffer.position(frameStart + 1); continue out; } } byte[] dataLengthBytes = new byte[] { mByteBuffer.get(8), mByteBuffer.get(9) };//第9个字节 为 数据长度.两个字节,所以是8,9 int dataLen = (int) ByteUtil.bytes2long(dataLengthBytes, 0, 2); /*dataLen已经包含了*/ if (dataLen > Protocol.MAX_DATA_N) { // 如果data n超过最大允许长度,表示数据错乱了 //则回到“第二位”,继续找到下一个 帧头 mByteBuffer.position(frameStart + 2); continue; } int total = Protocol.MIN_PACK_LEN + dataLen - 2; /*-2 最小数据包长度中已经包含, dataLen中有最小数据包长度中的2个字节*/ // 如果可读数据小于总数据长度,表示不够,还有数据没接收 if (readable < total) { // 重置一下要处理的位置,并跳出循环 mByteBuffer.reset(); break; } mByteBuffer.reset(); // 拿到整个包 byte[] allPack = new byte[total]; mByteBuffer.get(allPack); byte[] recvCheck = new byte[2]; recvCheck[0] = allPack[allPack.length - 2]; recvCheck[1] = allPack[allPack.length - 1]; byte[] myCheck = ByteUtil.getXOR(allPack, allPack.length - 2); if (isValid(recvCheck, myCheck)) {// 校验通过 /**/ LgqLogutil.d("#####校验通过,allPack:" + ByteUtil.bytes2HexStr(allPack)); //validData.add(allPack); LogManager.instance().post(new RecvMessage(ByteUtil.bytes2HexStr(allPack))); } else { mByteBuffer.position(frameStart + 2); } } } catch (Exception e) { e.printStackTrace(); } finally { // 最后清掉之前处理过的不合适的数据 mByteBuffer.compact(); } //String hexStr = ByteUtil.bytes2HexStr(received, 0, size); //String hexStr = ByteUtil.bytes2HexStr(allPack, 0, size); //LogManager.instance().post(new RecvMessage(hexStr)); }
粘包解决方法2
try { //LogPlus.e("Receiver", "接收数据=" + ByteUtil.bytes2HexStr(bytes, offset, length)); byte b; int readable; mByteBuffer.put(bytes, 0, length); mByteBuffer.flip(); out: while ((readable = mByteBuffer.remaining()) >= Protocol.MIN_PACK_LEN) { /*只处理数据帧, 不处理响应帧*/ mByteBuffer.mark(); // 标记一下开始的位置 int frameStart = mByteBuffer.position(); byte[] frameHead = Protocol.FRAME_HEAD; for (byte h : frameHead) { b = mByteBuffer.get(); if (b != h) { // 不满足帧头,就跳到第二位,重新开始 mByteBuffer.position(frameStart + 1); continue out; } } byte[] dataLengthBytes = new byte[]{mByteBuffer.get(4)};//第5个字节 为 数据长度 int dataLen = (int) ByteUtil.bytes2long(dataLengthBytes, 0, 1); /*dataLen已经包含了*/ if (dataLen > Protocol.MAX_DATA_N) { // 如果data n超过最大允许长度,表示数据错乱了 //则回到“第二位”,继续找到下一个 帧头 mByteBuffer.position(frameStart + 2); continue; } int total = Protocol.MIN_PACK_LEN + dataLen - 2; /*-2 最小数据包长度中已经包含, dataLen中有最小数据包长度中的2个字节*/ // 如果可读数据小于总数据长度,表示不够,还有数据没接收 if (readable < total) { // 重置一下要处理的位置,并跳出循环 mByteBuffer.reset(); break; } mByteBuffer.reset(); // 拿到整个包 byte[] allPack = new byte[total]; mByteBuffer.get(allPack); byte recvCheck = allPack[allPack.length - 1]; byte myCheck = ByteUtil.getXOR(allPack, 0, allPack.length - 1); if (myCheck == recvCheck) { // 校验通过 /**/ LogPlus.d("#####校验通过,allPack:" + ByteUtil.bytes2HexStr(allPack)); validData.add(allPack); } else { mByteBuffer.position(frameStart + 2); } } } catch (Exception e) { e.printStackTrace(); } finally { // 最后清掉之前处理过的不合适的数据 mByteBuffer.compact(); }
/**
* 字节数组转换成对应的16进制表示的字符串
*
* @param src
* @return
*/
public static String bytes2HexStr(byte[] src) {
StringBuilder builder = new StringBuilder();
if (src == null || src.length <= 0) {
return "";
}
char[] buffer = new char[2];
for (int i = 0; i < src.length; i++) {
buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
builder.append(buffer);
}
return builder.toString().toUpperCase();
}
1、心跳数据强转为 byte[] 数据类型
@Override
public RecvCommand adaptReceive(byte[] allPack, Object... other) {
int cmd = (int) other[0];
LogPlus.e( "收到的命令:" +cmd);
byte[] data = (byte[]) other[1];
2、开始解析——必须根据解析协议才能解析数据
public class RecvA3Status extends BaseRecvCommand {
private final int mResult;
private final float mPowerFactorState;
public List<AuxiliaryPlate> auxiliaryPlates;
public RecvA3Status(byte[] allPack, byte[] data) {
super(allPack, data);
mResult = 0xff & data[0];
mPowerFactorState = ByteUtil.getFloat(data, 25);
auxiliaryPlates = new ArrayList<>();
for (int i = 0; i < (data.length - 29) / 107; i++) {
byte[] bytes = new byte[107];
System.arraycopy(data, 29 + i * 107, bytes, 0, bytes.length);
if (i==0){
LogPlus.e("几号门==="+i, "门状态11接收数据2仓门=" + ByteUtil.bytes2HexStr(bytes));
}else {
LogPlus.i("几号门==="+i, "门状态11接收数据2仓门=" + ByteUtil.bytes2HexStr(bytes));
}
auxiliaryPlates.add(new AuxiliaryPlate(bytes));
}
}
/**
* 仓控板状态表
*/
public static class AuxiliaryPlate {
private int onlineState;
private int doorState;
private int batteryState;
private int fireState;
private int urgentState;
private int temperature;
private int batteryVoltage;
private int chargerOnlineState;
private ChargerState chargerState;
private OnOffAmount onOffAmount;
private AnalogQuantity analogQuantity;
private int BMSOnlineState;
private String BMSID;
/**type 0 = 正常电池样式 1 = 显示屏样式 */
public int type = 0;
public AuxiliaryPlate(byte[] bytes) {
onlineState = 0xff & bytes[0];
doorState = 0xff & bytes[1];
batteryState = 0xff & bytes[2];
fireState = 0xff & bytes[3];
urgentState = 0xff & bytes[4];
temperature = 0xff & bytes[5];
batteryVoltage = ByteUtil.bytes2long(bytes, 6, 2) / 100;
chargerOnlineState = 0xff & bytes[8];
byte[] bytes1 = new byte[6];
System.arraycopy(bytes, 9, bytes1, 0, bytes1.length);
chargerState = new ChargerState(bytes1);
BMSOnlineState = 0xff & bytes[15];
byte[] bytes2 = new byte[26];
System.arraycopy(bytes, 16, bytes2, 0, bytes2.length);
BMSID = new String(bytes2, StandardCharsets.US_ASCII).trim();
byte[] bytes3 = new byte[7];
System.arraycopy(bytes, 42, bytes3, 0, bytes3.length);
onOffAmount = new OnOffAmount(bytes3);
byte[] bytes4 = new byte[58];
System.arraycopy(bytes, 49, bytes4, 0, bytes4.length);
analogQuantity = new AnalogQuantity(bytes4);
}
付解析协议:
实现demo:https://download.csdn.net/download/meixi_android/12707609
bug在线交流: QQ1085220040