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再进行分析