Android 串口开发——粘包解决方法,定时查询心跳数据,解析心跳数据。——持续更新中

粘包解决方法

 方法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,9int 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/414341.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

17-Flutter移动电商实战-首页_楼层区域的编写

1、楼层标题组件 该组件非常简单&#xff0c;只接收一个图片地址&#xff0c;然后显示即可&#xff1a; class FloorTitle extends StatelessWidget {final String picture_address;FloorTitle({this.picture_address});overrideWidget build(BuildContext context) {return …

IIS Server Farms入门

概念了解 IIS Server Farms&#xff0c;实际上应该叫“Microsoft Web Farm Framework (WFF)”&#xff0c;依赖于“Web Platform Installer”才能安装&#xff0c;依赖于WebDeploy组件。可参见 Web Farm Framework Setting up a Server Farm with the Web Farm Framework 2.0 f…

软件工程导论团队项目

软件工程导论团队项目&#xff08;速课小龙&#xff09; 一&#xff0e;团队介绍 1.团队成员 邓 旭 2016012068 &#xff08;组长&#xff09; 陈逸璇 2016012056 周紫伊 2016012043 谢月鹏 2016012066 洪泽芳 2016011988 葛金星 2016011992 2.队名&#xff1a;速课小龙 3.项…

面试必备:HashMap、Hashtable、ConcurrentHashMap的原理与区别

本文转载自 夏雪冬日&#xff1a;https://www.cnblogs.com/heyonggang/p/9112731.html 在实际面试过程中出现集合 Map 的概率接近 100%&#xff0c;可见不背上个 Map 相关的题目都不好意思去面试了。 如果你去面试&#xff0c;面试官不问你这个问题&#xff0c;你来找我^_^ 下…

Eclipse中将java类打成jar包形式运行

记录一次帮助小伙伴将java类打成jar包运行 1、创建java project项目 file > new > project > java project 随便起一个项目名称&#xff0c;finish 完成后项目结构如下&#xff1a; 2、植入java类 将准备好的java类&#xff0c;植入项目中&#xff0c;在 src 目录中…

工作274:ele-图标使用

/*各个路由模块的建立*/ import Home from "/views/Home/Home"; import GlobalLayout from "/layout/GlobalLayout"; import Login from "/views/login/Login"; import Check from "/views/login/Check"; const SYSTEM_MANAGEMENT &q…

串口开发,数据类型转换——字符串转 byte[],byte[]转二进制,二进制转十进制转byte[],byte[]转十进制,byte[]拼接,校验

bytez转String /*** 字节数组转换成对应的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];…

dataBinding和retrofit的使用

1、dataBinding使用方法 1、配置dataBinding 2、创建layout类型xml布局文件 3、即可引用 ActivityMainBinding mainBinding; mainBinding DataBindingUtil.setContentView(this, R.layout.activity_main); mainBinding.testte.setText("abcccc"); 2、retrofit的…

使多个线程循环输出0-99-0-99

直接上代码&#xff0c;后面我也有一个问题&#xff0c;关于对象的notifyAll或者notify或者single或者singleAll&#xff0c;唤醒线程是顺序唤醒吗&#xff0c;我这里都是顺序输出了 关于公平与非公平锁的问题&#xff1f;唤醒是公平的&#xff1f;&#xff1f;&#xff1f;又没…

66-Flutter移动电商实战-会员中心_编写ListTile的通用方法

1、界面分析 通过下图我们可以拆分成 4 部分&#xff0c;头部、订单标题区域、订单列表区域、ListTitle同用部分。 2、UI编写 2.1、头部 主要用到了圆形头像裁剪组件-ClipOval 顶部头像区域Widget _topHeader(){ return Container( width: ScreenUtil().setWidth(750), …

Android板实现双屏显示,DisplayManager和Display的使用

非常简单。 效果 1、创建分屏管理类 DisplayController public class DisplayController {public static Display getTargetDisplay() {DisplayManager displayManager (DisplayManager) App.getInstance().getSystemService(Context.DISPLAY_SERVICE);Display[] presentat…

毕业论文管理系统(类图,er图,时序图)

转载于:https://www.cnblogs.com/huahua985/p/8732595.html

67-Flutter中高德地图插件的使用

1、注册和建立高德API应用 高德网站&#xff1a;https://lbs.amap.com/ 控制台-应用管理-创建应用 在创建 Key 2、获得SHA1 进入Flutter项目中的android文件夹内&#xff0c;打开任意一个文件&#xff1a; 比如进入 build.gradle&#xff0c;右上角会有 Open for Editing an…

第八篇Django分页

Django分页 1.复杂版 data []for i in range(1, 302):tmp {"id": i, "name": "alex-{}".format(i)}data.append(tmp)print(data)def user_list(request):# user_list data[0:10]# user_list data[10:20]try:current_page int(request.GET.g…

20179214 《网络攻防实践》第五周学习

20179214 《网络攻防实践》第五周学习 web应用程序体系结构及其安全威胁 web应用程序体系结构 浏览器 标准的web客户端&#xff0c;Web服务器 通常被简单的描述为http守护程序&#xff0c;接受web客户端对资源的请求。Web应用程序 是处于服务器端的业务逻辑&#xff0c;最普遍的…