concox协议与部标协议存在着很大的不同,特别是包头包尾的定义,部标使用的是打个byte位,然后通过转义的方式来实现,这也是目前主流的处理方式,无论是808,809还是1078与苏标都是单字节的包头包尾,而concox则使用的是双字节来定义包头包尾,中间无转义的方式,其实也有很多私有协议使用的是双字节的包头包尾。

话不多说,我们首先看一下concox的协议格式(以登录包为例):

长度

描述

起始位

2

0x78 0x78

包长

1

长度= 协议号 + 信息内容 +信息序列号 + 错误校验

协议号

1

0x01

信息内容

终端ID

8

例:如果IMEI是 123456789123456,终端ID 为:0x01 0x23 0x45 0x67 0x89 0x120x34 0x56

类型识别码

2

根据此识别码判断终端类型

时区语言

2

时区语言标志,详见下表

信息序列号

2

从开机后,每次发送数据序列号都自动加1

错误校验

2

“包长度”到“信息序列号”的CRC-ITU值。接收方若收到的信息计算有CRC错误,则忽略,抛弃这个数据包(算法详见附件1)

停止位

2

固定值: 0x0D 0x0A

Example: 78 78 11 01 07 52 53 36 78 90 02 42 70 00 32 01 00 05 12 79 0D 0A

首先我们定义一个解码器,写一个初步解析方法叫decodeMessage()

 private ConcoxMessage decodeMessage(ChannelHandlerContext ctx, ByteBuf in) {//可读长度不能小于基本长度if (in.readableBytes() < ConcoxConstant.MSG_BASE_LENGTH) {return null;}//防止字节流攻击,数据太大为异常数据if (in.readableBytes() > ConcoxConstant.MSG_MAX_LENGTH) {in.skipBytes(in.readableBytes());return null;}//初始化包长int dataLen = 0;//包长度字节长度int length=0;while (true) {//查找消息头,如果未找到则丢弃所有数据in.markReaderIndex();//获取包头int head=in.readShort();if(head==0x7878){dataLen=in.readByte()&0xFF;length=1;//可读长度不能小于数据长度加包尾if (in.readableBytes() < dataLen+2) {in.resetReaderIndex();return null;}break;}else if(head==0x7979){dataLen=in.readShort();length=2;//可读长度不能小于数据长度加包尾if (in.readableBytes() < dataLen+2) {in.resetReaderIndex();return null;}break;}//可读长度不能小于基本长度if (in.readableBytes() < ConcoxConstant.MSG_BASE_LENGTH) {in.resetReaderIndex();return null;}}ConcoxMessage concoxMessage = new ConcoxMessage();concoxMessage.setProtocolType(protocolType);//协议号int msgId = in.readByte()&0xFF;concoxMessage.setMsgId(msgId);//终端号与消息体处理if(msgId==0x01) {//读取终端号码byte[] terminalNumArr = new byte[8];in.readBytes(terminalNumArr);String terminalNum = ByteBufUtil.hexDump(terminalNumArr);concoxMessage.setTerminalNum(terminalNum.replaceAll("^(0+)", ""));//读取剩余消息体(信息内容(去掉设备号码)+信息序列号)byte[] remainingMsgBodyArr  = new byte[dataLen-11];in.readBytes(remainingMsgBodyArr);//组合完整消息体(包长度+协议号+信息内容+信息序列号)ByteBuf frame= ByteBufAllocator.DEFAULT.heapBuffer(dataLen +1);//包长度frame.writeByte(dataLen);//协议号frame.writeByte(msgId);//终端号码frame.writeBytes(terminalNumArr);//剩余消息体frame.writeBytes(remainingMsgBodyArr);concoxMessage.setMsgBody(frame);}else{//读取终端号码String terminalNum = SessionUtil.getTerminalInfo(ctx).getTerminalNum();concoxMessage.setTerminalNum(terminalNum);//读取剩余消息体(消息内容+序列号),去掉错误校验byte[] remainingMsgBodyArr = new byte[dataLen - 3];in.readBytes(remainingMsgBodyArr);//组合完整消息体(包长度+协议号+信息内容+信息序列号)//包长度字节+协议号+消息内容+消息序列号(等同于消息长度-错误校验+包长度的字节)int msgLen = dataLen + length - 2;ByteBuf frame = ByteBufAllocator.DEFAULT.heapBuffer(msgLen);if (length == 1) {//包长度frame.writeByte(dataLen);} else {frame.writeShort(dataLen);}//协议号frame.writeByte(msgId);//剩余消息体(消息内容+序列号)frame.writeBytes(remainingMsgBodyArr);concoxMessage.setMsgBodyArr(remainingMsgBodyArr);concoxMessage.setMsgBody(frame);}//读取校验in.readShort();//读取包尾in.readShort();// 回收已读字节in.discardReadBytes();return concoxMessage;}

因为包头是两个字节,所以我们先找到设备传输过来的原始数据的包头,不过concox包头有两种分别是0x78 0x78,0x79 0x79,所以我们这里找两个连续字节分别是0x78 0x78或者0x79 0x79,循环查找,直到找到包头为止。

找的过程中需要根据剩余的字节数去判断是否小于数据的基本长度,如果小于基本长度则重置读指针然后等待下个包的到来,不然数据是不完整的,即便进入后续的解析也是有问题的。

//可读长度不能小于基本长度if (in.readableBytes() < ConcoxConstant.MSG_BASE_LENGTH) {in.resetReaderIndex();return null;}

如果找到包头后,则读取协议号,因为concox的协议号是一个字节,我们这时候转成int的时候需要进行与运算,不过也可以将协议号定义成byte,这样就不需要进行与运算了。

这里说一下为什么要进行与运算:

在做byte -> int类型转换时,JVM会做一个补位处理,
(注:补位是补1还是补0,取决于byte的最高位是1还是0)
以协议中出现的0x8A的协议号为例,转成二进制为:10001010,
如果直接赋值int值后是其实在计算机存储的是11111111 11111111 11111111 10001010(32位),
这个时候其实与最初的0x8A已经完全不等了,所以我们需要对其进行与0xFF做与运算,
可以将高24位置为0,低8位保持不变,这样做就可以保证和二进制补码的一致了。

因此我们在做协议解析的时候一定要注意将readByte()得到的数值与0xFF做一次与计算!

在登录过后设备需要进行回复确认,按照协议进行组包:

登录包回复(平台回复)

长度

描述

起始位

2

0x78 0x78

包长

1

长度 = 协议号 + 信息内容 +信息序列号 + 错误校验

协议号

1

0x01

信息序列号

2

从开机后,每次发送数据序列号都自动加1

错误校验

2

“包长度”到“信息序列号”的CRC-ITU值。接收方若收到的信息计算有CRC错误,则忽略,抛弃这个数据包(算法详见附件1)

停止位

2

固定值: 0x0D 0x0A

Example:78 78 05 01 00 05 9F F8 0D 0A

public static void replyMessage(ChannelHandlerContext ctx,int msgId,int index) {ByteBuf byteBuf = ByteBufAllocator.DEFAULT.heapBuffer(10);//包长度byteBuf.writeByte(5);//协议号byteBuf.writeByte(msgId);//信息序列号byteBuf.writeShort(index);//读取消息体byte[] bodyArr = new byte[byteBuf.readableBytes()];byteBuf.readBytes(bodyArr);//获取crcint crc=(CommonUtil.GetCrc16(bodyArr, bodyArr.length));//写入包头byteBuf.writeByte(0x78);byteBuf.writeByte(0x78);//写入包长度byteBuf.writeByte(5);//协议号byteBuf.writeByte(msgId);//信息序列号byteBuf.writeShort(index);//错误校验byteBuf.writeShort(crc);byteBuf.writeByte(0x0D);byteBuf.writeByte(0x0A);log.info("回复确认包:{}", ByteBufUtil.hexDump(byteBuf));ctx.writeAndFlush(byteBuf);}

这个时候设备就可以完成正常登录了,后面的解析可参考以上解析思路进行,下面附上协议中错误校验(CRC-ITU)的方法实现

public static char GetCrc16(byte[] pData, int nLength){int i=0;// 初始化char fcs = 0xffff;while(nLength>0){fcs = (char) ((fcs >> 8) ^ crctab16[(fcs ^ pData[i]) & 0xff]);nLength--;i++;}// 取反return (char) ~fcs;}

做物联网的朋友可以一起交流学习哦!QQ:571521973

使用Java Netty做Concox协议解析相关推荐

  1. netty对http协议解析原理解析

    转载自 https://blog.csdn.net/xiangzhihong8/article/details/52029446 本文主要介绍netty对http协议解析原理,着重讲解keep-ali ...

  2. java版银联8583协议解析,超简单超直观的实现及示例(全互联网最简单)

    一直以来做嵌入式软件开发,跟银联8583协议通信打交道太多了. 最近有需要把8383协议的解析用到android上,但是搜遍了整个互联网,没发现有哪个简单好用点的java版8583解析库.就自己动手自 ...

  3. http协议解决粘包拆包半包 的编码解码过程、 以及netty 使用http协议的原理

    本文主要介绍netty对http协议解析原理,着重讲解keep-alive,gzip,truncked等机制,详细描述了netty如何实现对http解析的高性能. 1 http协议 1.1 描述 标示 ...

  4. TDS协议解析(转载)

    最近在做TDS协议解析,但国内很少有TDS的资料,特此转载从国外一个网站弄来的TDS资料,不是特别全,可能也有些乱(比如今天做的RPC包的解析,看了好久才看明白,有机会的话我把RPC解析贴出来,RPC ...

  5. Java开发中Netty线程模型原理解析!

    Java开发中Netty线程模型原理解析,Netty是Java领域有名的开源网络库具有高性能和高扩展性的特点,很多流行的框架都是基于它来构建.Netty 线程模型不是一成不变的,取决于用户的启动参数配 ...

  6. java netty modbus协议接收iot数据

    IoTserver 源代码开源在gitee上 : IoT netty java gitee server sample c++ libuv 的IoT tcp server IoT c++ libuv ...

  7. JAVA自定义协议解析

    在JAVA中, 一般来说自定义协议都是涉及到物联网平台.字节数组和对象操作特别多. 使用netty, 然后使用byteBuffer 进行解包 或者 封包操作. 平时来说这个是没什么问题的. 但是 很多 ...

  8. Modbus通信协议+Modbus串口调试工具+Java版协议解析源码

    网络与串口二合一调试助手TCPCOM: https://download.csdn.net/download/liuyuan_java/87454762 Modbus调试工具,模拟串口调试工具 htt ...

  9. dubbo协议_Dubbo协议解析与OPPO自研ESA RPC框架实践

    本文来自OPPO互联网基础技术团队,转载请注名作者.同时欢迎关注我们的公众号:OPPO_tech,与你分享OPPO前沿互联网技术及活动. 1. 背景 Dubbo是一款高性能.轻量级的开源Java RP ...

最新文章

  1. 闲话机器人领域的国际会议
  2. “约见”面试官系列之常见面试题之第一百零一篇之vue-router传参(建议收藏)
  3. 定义一个类:实现功能可以返回随机的10个数字,随机的10个字母, 随机的10个字母和数字的组合;字母和数字的范围可以指定,类似(1~100)(A~z)...
  4. AIOps中异常检测的简单应用
  5. 台式机linux_什么将驱动主流台式机Linux?
  6. javascript 函数的几种声明函数以及应用环境
  7. Oracle 备份与恢复学习笔记(14)
  8. 电子元件 —— 二极管
  9. Pow(x, n) leetcode
  10. 关于性格内向者的10个误解,献给奋战在一线的程序员
  11. 考勤打卡记录数据库表结构_考勤系统数据表结构
  12. wpd小波包分解_基于奇异值分解和小波包分解的故障检测
  13. #蓝桥杯嵌入式#电路模电基础知识
  14. 《小岛经济学》读书笔记
  15. Siebel Open UI
  16. 测试--插拔寿命测试
  17. 网盘翻车不断,我是如何低价自建一个自用网盘
  18. 动态规划java实现数塔问题_动态规划入门_数塔问题
  19. 库卡机器人会卡顿吗_看完你就知道德国库卡机器人到底有多牛!
  20. 洛谷p4230 连环病原体 题解

热门文章

  1. java json 枚举_java枚举类型JSON格式返回处理
  2. IOS8中屏幕旋转问题解决
  3. 视频直播类App SDK盘点
  4. Nvidia显卡驱动 标准版(Standard)下载地址
  5. 54.Linux 网络编程
  6. MATLAB画图——设置轴标签不同字体
  7. HUAWEI HiAI亮相华为开发者生态大会 助力应用AI开发实现加速度
  8. 【论文阅读】Gosig: A Scalable and High-Performance Byzantine Consensus for Consortium Blockchains
  9. 【亡羊补牢】挑战数据结构与算法 第19期 LeetCode 212. 单词搜索 II(字典树,附上JS模板)
  10. 开启MicroPython多线程模式