java串口传输加帧头帧尾、解决丢包、沾包问题

网上寻找一圈只有简单的串口发送以及监听接收代码,很好奇大家不用解决丢包、沾包问题的吗??
可能没找到吧,写一个吧
为了识别是完整的包,加入了帧头“ECECECEC”,帧尾“FCFCFCFC”,可以根据需求自行修改

前情提要

private String hexStr="";//全局变量,用于将每次接收的byte[]转换成的hex字符串接在后面
 private List<String> dataList=new ArrayList<>();//全局变量,用于存储分割的完整的数据包已除去帧头帧尾
/**
     * 字节数组转hex字符串
     * @param b
     * @return
     */
    public String printHexString(byte[] b) {

        StringBuffer sbf = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sbf.append(hex.toUpperCase());
        }
        return sbf.toString().trim();
    }

核心代码,没写全,这个代码是放在串口监听的接收代码后面的,接收到byte[]后进行处理,需要注意,如果帧头帧尾位数与我的不同(8),代码中的8是要改的

if(data!=null&&data.length>0){
                    //为解决C#和JAVA的编码方式不同而存在的乱码问题,现将两边的都使用base64加密传输再加密
//                    byte[] decoded = Base64.getDecoder().decode(data);
                    //获取hex字符串
                    String dataOriginal = printHexString(data);
                    hexStr+=dataOriginal;
                    if(StringUtils.isNotBlank(hexStr)){
                        String[] hexStrs=hexStr.split("FCFCFCFC");
                        if(hexStrs!=null){
                            if(hexStrs.length>0){
                                if(hexStrs.length==1){
                                    String[] headers=hexStrs[0].split("ECECECEC");
                                    if(headers!=null){
                                        //length=0时刚好是帧头,不处理
                                        if(headers.length==1){
                                            if(headers[0].length()==hexStrs[0].length()){
                                                //说明没有帧头(在数据的最前又没有帧头,直接丢弃)
                                                hexStr="";
                                            }else {
                                                //有帧头
                                                if(hexStrs[0].length()!=hexStr.length()){
                                                    //有帧尾,完整的包插入分析队列,有帧头没帧尾的保留,继续接收数据(头尾相连应该丢弃)
//                                                    dataList.add(headers[0]);
                                                    hexStr="";
                                                }else {
                                                    //切帧头只有一包说明以帧头结尾又没有帧尾,补一个帧头
                                                    hexStr="ECECECEC";
                                                }
                                            }
                                        }
                                        if(headers.length>1){
                                            //有帧头
                                            //一个帧尾或无帧尾
                                            if(hexStrs[0].length()!=hexStr.length()){
                                                //说明有帧尾,丢弃有头没尾的前面部分,留下头的最后一部分
                                                dataList.add(headers[headers.length-1]);
                                                hexStr="";

                                            }else {
                                                //说明没有帧尾,无帧头丢弃
                                                hexStr="ECECECEC"+headers[headers.length-1];
                                            }
                                        }
                                        if(headers.length==0){
                                            //说明数据只有头,若是有尾,说明数据丢包,将其去掉,若是没尾则不处理
                                            if(hexStrs[0].length()!=hexStr.length()){
                                                //说明有帧尾,丢弃有头没尾的前面部分,留下头的最后一部分
                                                hexStr="";
                                            }
                                        }
                                    }
                                }
                                if(hexStrs.length>1){
                                    for(int i=0;i<hexStrs.length;i++){
                                        String[] headers=hexStrs[i].split("ECECECEC");
                                        if(i==0){
                                            if(StringUtils.isNotBlank(hexStrs[i])){
                                                //headers.length==1或==0说明头尾在一起,中间数据丢失,丢弃
                                                if(headers.length>1){
                                                    dataList.add(headers[headers.length-1]);
                                                }
                                            }
                                        }else if(i==hexStrs.length-1){
                                            if(headers!=null){
                                                if(headers.length==1){
                                                    String sub=hexStr.substring(hexStr.length()-8,hexStr.length());
                                                    if(StringUtils.isNotBlank(sub)&&sub.equals("FCFCFCFC")){
                                                        hexStr="";
                                                    }else {
                                                        if(headers[0].equals(hexStrs[hexStrs.length-1])){
                                                            //说明末尾没有帧头,丢弃
                                                            hexStr="";
                                                        }else {
                                                            //说明末尾是帧头,补帧头
                                                            hexStr="ECECECEC";
                                                        }

                                                    }
                                                }
                                                if(headers.length>1){
                                                    //有帧头
                                                    String sub=hexStr.substring(hexStr.length()-8,hexStr.length());
                                                    if(StringUtils.isNotBlank(sub)&&sub.equals("FCFCFCFC")){
                                                        //有帧头帧尾则是一包完整数据
                                                        dataList.add(headers[headers.length-1]);
                                                        hexStr="";
                                                    }else {
                                                        //有帧头没有帧尾
                                                        hexStr="ECECECEC"+headers[headers.length-1];
                                                    }

                                                }
                                                if(headers.length==0){
                                                    //说明数据只有头
                                                    String sub=hexStr.substring(hexStr.length()-8,hexStr.length());
                                                    if(StringUtils.isNotBlank(sub)&&sub.equals("FCFCFCFC")){
                                                        //头尾相连丢弃
                                                        hexStr="";
                                                    }else {
                                                        //有帧头没有帧尾补帧头
                                                        hexStr="ECECECEC";
                                                    }
                                                }
                                            }
                                        }else {
                                            //中间的部分肯定有尾,只要有头就插入,没有就丢弃(不用管,处理了最后的自然就会丢弃)
                                            if(headers!=null){
                                               // headers.length==0时候说明帧头帧尾相连,丢弃
                                               // headers.length==1时候说明帧头帧尾相连或者没有帧头,丢弃
                                                if(headers.length>1){
                                                    //头的最后一包是有头有尾的
                                                    dataList.add(headers[headers.length-1]);
                                                }
                                            }
                                        }
                                    }
                                }


                            }else if(hexStrs.length==0){
                                //出现这种情况则说明hexStr="FCFCFCFC",故丢弃
                                hexStr="";
                            }
                        }else {
                            //其实不会为空
                            hexStr="";
                        }
                    }
                }

代码初步运行是没问题的,有问题大家自行修改,欢迎与我讨论,大家可以根据需求增加指令位和数据长度和校验位等等,我的指令也写在数据里面了的,反序列化到model再进行分析