数据接收黏包丢包简单处理,适用蓝牙、串口、socket传输

 

黏包现象:

比如需要分2包发送 01 02 03 04 05 和AA BB

结果接收端可能会接收成一包01 02 03 04 05 AA BB

或则接收成2包01 02 03 04 和05 AA BB

丢包现象:

比如需要分2包发送 01 02 03 04 05 和AA BB

结果接收端可能会接收到01 02 03 04  AA BB(数据05丢失)

如果它按5+2分包,会分成2包01 02 03 04  AA 和 BB

 

处理办法:定制数据协议,添加缓存机制,收到数据后添加到缓存,在缓存中解析出每包完整数据,如果丢包,错包,也不影响其他包的数据处理

 

示例代码如下:

 

 //本方法可能存在干扰数据正好与hed相等,然后第二位是干扰数据或者是真的数据hed,此时把第二位当数据长度会出现问题
 //比如缓存中有数据3B 28 3B 09 ..... 0A,其中3B 28为干扰数据,后面还有个完整的数据包,
//那么此方法会等到再有数据进来,缓存中有0X28个数据时才会判断出3B 28为无效数据,就造成了延时问题,
 //或者后面不会再有数据进来,那么后面那个真的完整数据就会存在缓存中而没有拿出来使用。
 //解决办法:
  //一、增加hed头为多个字节,减少干扰和hed的相等的几率。
  //二、继续向后面检查是否有完整数据,但是检测前不清除前面的buff,如果有的话再清空buff中的使用数据和前面的无效数据

public class test {

    public static void main(String[] args) {

        VData  vData = new VData();

        byte[] data1 = {0X3B, 0X09, 0X01, 0X02, 0X03, 0X04, 0X05, 0X53, 0X0A};
        byte[] data2 = {0X3B, 0X06, (byte) 0XAA, (byte) 0XBB, (byte) 0XA6, 0X0A};
        byte[] data3 = {0X3B, 0X0B, 0X01, 0X02, 0X03, 0X04, 0X05, (byte) 0XAA, (byte) 0XBB, (byte) 0XBA, 0X0A};

        System.out.println("发送数据:" +Arrays.toString(data1));
        for (byte b : data1) {
            vData.buffData(b);
        }

        System.out.println("发送数据:" +Arrays.toString(data2));
        for (byte b : data2) {
            vData.buffData(b);
        }

        System.out.println("发送数据:" +Arrays.toString(data3));
        for (byte b : data3) {
            vData.buffData(b);
        }


        byte[] data4 = concat(concat(data1,data2),data3);
        System.out.println("黏包发送:"+Arrays.toString(data4));
        for (byte b : data4) {
            vData.buffData(b);
        }


        byte[] data5 = concat(concat(new byte[]{0X03, 0X04, 0X05, 0X53, 0X0A},data2),data3);
        System.out.println("错误发送:"+Arrays.toString(data5));
        for (byte b : data5) {
            vData.buffData(b);
        }


        byte[] data6 = concat(concat(data1,new byte[]{0X03, 0X04, 0X05, 0X53, 0X0A}),data3);
        System.out.println("错误发送2:"+Arrays.toString(data6));
        for (byte b : data6) {
            vData.buffData(b);
        }



    }


    static byte[] concat(byte[] a, byte[] b) {
        byte[] c= new byte[a.length+b.length];
        System.arraycopy(a, 0, c, 0, a.length);
        System.arraycopy(b, 0, c, a.length, b.length);
        return c;
    }


}

class VData {


    /**
     * 假设正确格式为(如: 3B 09 01 02 03 04 05 53 0A)
     * 3B  X1  X2....Xn  Xm  0A
     * 3B头  X1长度   X2-Xn数据  Xm校验  0A结束
     * X1长度为3B到0A所有字节的长度
     * Xm=(3B+X1+X2+...+Xn)&0XFF
     */

    //每一包数据最大长度
    private int dataMaxSize = 50;
    //缓存区长度
    private int buffSize = dataMaxSize * 2;
    //缓存区
    private byte[] buff = new byte[buffSize];
    //缓存区可存入的下一个数据下标(即已存入的数据长度)
    private int buffIndex = 0;
    //头
    private byte hed = 0X3B;
    //尾
    private byte end = 0X0A;
    //输出
    private byte[] outBuff;

    //加入缓存
    public void buffData(byte oneByte) {
        if (buffIndex >= buff.length) {
            //缓存区已满,删除第一个
            if (buff.length - 1 >= 0) {
                System.arraycopy(buff, 1, buff, 0, buff.length - 1);
                buff[buff.length - 1] = 0X00;
                buffIndex = buff.length - 1;
            }
        }
        buff[buffIndex] = oneByte;
        buffIndex++;
        //新数据已加入缓存,从缓存中检验是否有完整数据
        VerificationData();

    }

    //新数据已加入缓存,从缓存中检验是否有完整数据
    private void VerificationData() {

        //System.out.println("BUFF:" +Arrays.toString(buff));

        if (buffIndex < 4) {
            //头+长度+data +校验+尾 就算数据为空也至少要4位
        } else {
            if (buff[0] == hed) {
                //数据长度
                int dataLength = buff[1] & 0XFF;
                //System.out.println("dataLength:" +dataLength);
                if (dataLength <= dataMaxSize) {
                    if (buffIndex >= dataLength) {
                        if (buff[dataLength - 1] == end) {
                            int CRC = 0;
                            for (int i = 0; i < dataLength - 2; i++) {
                                CRC = CRC + buff[i];
                            }

                            if (CRC == buff[dataLength - 2]) {
                                //是条完整的数据
                                outBuff = new byte[dataLength];
                                System.arraycopy(buff, 0, outBuff, 0, dataLength);
                                //use outBuff
                                useOutBuff(outBuff);

                                //清除已使用数据
                                System.arraycopy(buff, dataLength, buff, 0, dataLength);
                                for (int i = 0; i < dataLength; i++) {
                                    buff[buffIndex - dataLength + i] = 0X00;
                                }

                                buffIndex = buffIndex - dataLength;
                                //再次检验
                                VerificationData();

                            } else {
                                System.out.println("校验:"+CRC);
                                //校验不相等
                                System.arraycopy(buff, 1, buff, 0, buffIndex - 1);
                                buff[buffIndex - 1] = 0X00;
                                buffIndex = buffIndex - 1;
                                //再次检验
                                VerificationData();
                            }


                        } else {
                            //数据尾对不上,不是一条数据
                            System.arraycopy(buff, 1, buff, 0, buffIndex - 1);
                            buff[buffIndex - 1] = 0X00;
                            buffIndex = buffIndex - 1;
                            //再次检验
                            VerificationData();
                        }

                    } else {

                        //已缓存的数据还没有这没多,继续等待缓存
                        //本方法可能存在干扰数据正好与hed相等,然后第二位是干扰数据或者是真的数据hed,此时把第二位当数据长度会出现问题
                        //比如缓存中有数据3B 28 3B 09 ..... 0A,其中3B 28为干扰数据,后面还有个完整的数据包,
                        //那么此方法会等到再有数据进来,缓存中有0X28个数据时才会判断出3B 28为无效数据,就造成了延时问题,
                        //或者后面不会再有数据进来,那么后面那个真的完整数据就会存在缓存中而没有拿出来使用。
                        //解决办法:
                        //一、增加hed头为多个字节,减少干扰和hed的相等的几率。
                        //二、在此继续向后面检查是否有完整数据,但是检测前不清除前面的buff,如果有的话再清空buff中的使用数据和前面的无效数据

                    }

                } else {
                    //数据长度超过规定长度,肯定是错误数据,舍弃1位
                    System.arraycopy(buff, 1, buff, 0, buffIndex - 1);
                    buff[buffIndex - 1] = 0X00;
                    buffIndex = buffIndex - 1;
                    //再次检验
                    VerificationData();
                }

            } else {
                //第一个数据不是hed,舍弃
                System.arraycopy(buff, 1, buff, 0, buffIndex - 1);
                buff[buffIndex - 1] = 0X00;
                buffIndex = buffIndex - 1;
                //再次检验
                VerificationData();
            }
        }
    }

    private void useOutBuff(byte[] outBuff) {
        System.out.println("收到数据:" + Arrays.toString(outBuff));
    }


}

 

 

 

 

运行结果如下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值