java解析JT808协议的实现代码⽬录
1 JT808协议扫盲
1.1 数据类型
1.2 消息结构
1.3 消息头
2 解析
2.1 消息体实体类
2.2 字节数组到消息体实体类的转换
2.3 和netty结合
3 demo级别java⽰例
本篇⽂章将介绍JT808协议的解析思路。
另请⼤神绕路,不喜勿喷!
先写个⼤致的思路,有疑问可以联系本⼈,联系⽅式:
emial: hylexus@163
1 JT808协议扫盲
1.1 数据类型
数据类型描述及要求
BYTE⽆符号单字节整形(字节, 8 位)
WORD⽆符号双字节整形(字, 16 位)
DWORD⽆符号四字节整形(双字, 32 位)
BYTE[n]n 字节
BCD[n]8421 码, n 字节
STRING GBK 编码,若⽆数据,置空
1.2 消息结构
标识位消息头消息体校验码标识位
1byte(0x7e)16byte1byte1byte(0x7e)
1.3 消息头
消息ID(0-1)  消息体属性(2-3)  终端⼿机号(4-9)  消息流⽔号(10-11)    消息包封装项(12-15) byte[0-1]  消息ID word(16)
byte[2-3]  消息体属性 word(16)
bit[0-9]    消息体长度
bit[10-12]  数据加密⽅式
此三位都为 0,表⽰消息体不加密
第 10 位为 1,表⽰消息体经过 RSA 算法加密
其它保留
bit[13]    分包
1:消息体卫长消息,进⾏分包发送处理,具体分包信息由消息包封装项决定
0:则消息头中⽆消息包封装项字段
bit[14-15]  保留
byte[4-9]  终端⼿机号或设备ID bcd[6]
根据安装后终端⾃⾝的⼿机号转换
⼿机号不⾜12 位,则在前⾯补 0
byte[10-11]    消息流⽔号 word(16)
按发送顺序从 0 开始循环累加
byte[12-15]    消息包封装项
byte[0-1]  消息包总数(word(16))
该消息分包后得总包数
byte[2-3]  包序号(word(16))
从 1 开始
如果消息体属性中相关标识位确定消息分包处理,则该项有内容否则⽆该项
2 解析
整个消息体结构中最复杂的就是消息头了。
2.1 消息体实体类
以下是对整个消息体抽象出来的⼀个java实体类。
import java.nio.channels.Channel;
public class PackageData {
/
**
* 16byte 消息头
*/
protected MsgHeader msgHeader;
// 消息体字节数组
protected byte[] msgBodyBytes;
/**
* 校验码 1byte
*/
protected int checkSum;
//记录每个客户端的channel,以便下发信息给客户端
protected Channel channel;
public MsgHeader getMsgHeader() {
return msgHeader;
}
//TODO set 和 get ⽅法在此处省略
//消息头
public static class MsgHeader {
// 消息ID
protected int msgId;
/////// ========消息体属性
// byte[2-3]
protected int msgBodyPropsField;
// 消息体长度
protected int msgBodyLength;
// 数据加密⽅式
protected int encryptionType;
// 是否分包,true==>有消息包封装项
protected boolean hasSubPackage;
// 保留位[14-15]
protected String reservedBit;
/////// ========消息体属性
// 终端⼿机号
protected String terminalPhone;
// 流⽔号
protected int flowId;
//////// =====消息包封装项
// byte[12-15]
protected int packageInfoField;
// 消息包总数(word(16))
protected long totalSubPackage;
// 包序号(word(16))这次发送的这个消息包是分包中的第⼏个消息包, 从 1 开始    protected long subPackageSeq;
//////// =====消息包封装项
//TODO set 和 get ⽅法在此处省略
}
}
2.2 字节数组到消息体实体类的转换
2.2.1 消息转换器
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.hylexus.jt808.util.BCD8421Operater;
import cn.hylexus.jt808.util.BitOperator;
import cn.hylexus.jt808.vo.PackageData;
import cn.hylexus.jt808.vo.PackageData.MsgHeader;
public class MsgDecoder {
private static final Logger log = Logger(MsgDecoder.class);
private BitOperator bitOperator;
private BCD8421Operater bcd8421Operater;
public MsgDecoder() {
this.bitOperator = new BitOperator();
this.bcd8421Operater = new BCD8421Operater();
}
//字节数组到消息体实体类
public PackageData queueElement2PackageData(byte[] data) {
PackageData ret = new PackageData();
// 1. 16byte 或 12byte 消息头
MsgHeader msgHeader = this.parseMsgHeaderFromBytes(data);
ret.setMsgHeader(msgHeader);
int msgBodyByteStartIndex = 12;
// 2. 消息体
// 有⼦包信息,消息体起始字节后移四个字节:消息包总数(word(16))+包序号(word(16))
if (msgHeader.isHasSubPackage()) {
msgBodyByteStartIndex = 16;
}
byte[] tmp = new MsgBodyLength()];
System.arraycopy(data, msgBodyByteStartIndex, tmp, 0, tmp.length);
ret.setMsgBodyBytes(tmp);
/
/ 3. 去掉分隔符之后,最后⼀位就是校验码
// int checkSumInPkg =
// ByteToInteger(data[data.length - 1]);
int checkSumInPkg = data[data.length - 1];
int calculatedCheckSum = CheckSum4JT808(data, 0, data.length - 1);
ret.setCheckSum(checkSumInPkg);
if (checkSumInPkg != calculatedCheckSum) {
log.warn("检验码不⼀致,msgid:{},pkg:{},calculated:{}", MsgId(), checkSumInPkg, calculatedCheckSum);    }
return ret;
}
private MsgHeader parseMsgHeaderFromBytes(byte[] data) {
MsgHeader msgHeader = new MsgHeader();
// 1. 消息ID word(16)
// byte[] tmp = new byte[2];
// System.arraycopy(data, 0, tmp, 0, 2);
// msgHeader.setMsgId(this.bitOperator.twoBytesToInteger(tmp));
msgHeader.setMsgId(this.parseIntFromBytes(data, 0, 2));
// 2. 消息体属性 word(16)=================>
// System.arraycopy(data, 2, tmp, 0, 2);
// int msgBodyProps = this.bitOperator.twoBytesToInteger(tmp);
int msgBodyProps = this.parseIntFromBytes(data, 2, 2);
msgHeader.setMsgBodyPropsField(msgBodyProps);
/
/ [ 0-9 ] 0000,0011,1111,1111(3FF)(消息体长度)
msgHeader.setMsgBodyLength(msgBodyProps & 0x1ff);
// [10-12] 0001,1100,0000,0000(1C00)(加密类型)
msgHeader.setEncryptionType((msgBodyProps & 0xe00) >> 10);
// [ 13_ ] 0010,0000,0000,0000(2000)(是否有⼦包)
msgHeader.setHasSubPackage(((msgBodyProps & 0x2000) >> 13) == 1);
// [14-15] 1100,0000,0000,0000(C000)(保留位)
msgHeader.setReservedBit(((msgBodyProps & 0xc000) >> 14) + "");
// 消息体属性 word(16)<=================
// 3. 终端⼿机号 bcd[6]
// tmp = new byte[6];
/
/ System.arraycopy(data, 4, tmp, 0, 6);
// msgHeader.setTerminalPhone(this.bcd8421Operater.bcd2String(tmp));
msgHeader.setTerminalPhone(this.parseBcdStringFromBytes(data, 4, 6));
// 4. 消息流⽔号 word(16) 按发送顺序从 0 开始循环累加
// tmp = new byte[2];
// System.arraycopy(data, 10, tmp, 0, 2);
// msgHeader.setFlowId(this.bitOperator.twoBytesToInteger(tmp));
msgHeader.setFlowId(this.parseIntFromBytes(data, 10, 2));
// 5. 消息包封装项
// 有⼦包信息
if (msgHeader.isHasSubPackage()) {
/
/ 消息包封装项字段
msgHeader.setPackageInfoField(this.parseIntFromBytes(data, 12, 4));
// byte[0-1] 消息包总数(word(16))
// tmp = new byte[2];
// System.arraycopy(data, 12, tmp, 0, 2);
// msgHeader.setTotalSubPackage(this.bitOperator.twoBytesToInteger(tmp));
msgHeader.setTotalSubPackage(this.parseIntFromBytes(data, 12, 2));
// byte[2-3] 包序号(word(16)) 从 1 开始
// tmp = new byte[2];
// System.arraycopy(data, 14, tmp, 0, 2);
// msgHeader.setSubPackageSeq(this.bitOperator.twoBytesToInteger(tmp));
msgHeader.setSubPackageSeq(this.parseIntFromBytes(data, 12, 2));
}
return msgHeader;
}
protected String parseStringFromBytes(byte[] data, int startIndex, int lenth) {
return this.parseStringFromBytes(data, startIndex, lenth, null);
}
private String parseStringFromBytes(byte[] data, int startIndex, int lenth, String defaultVal) {
try {
byte[] tmp = new byte[lenth];
System.arraycopy(data, startIndex, tmp, 0, lenth);
字符串转数组工具类的方法
return new String(tmp, "UTF-8");
} catch (Exception e) {
<("解析字符串出错:{}", e.getMessage());
e.printStackTrace();
return defaultVal;
}
}
private String parseBcdStringFromBytes(byte[] data, int startIndex, int lenth) {
return this.parseBcdStringFromBytes(data, startIndex, lenth, null);
}
private String parseBcdStringFromBytes(byte[] data, int startIndex, int lenth, String defaultVal) {    try {
byte[] tmp = new byte[lenth];
System.arraycopy(data, startIndex, tmp, 0, lenth);
return this.bcd8421Operater.bcd2String(tmp);
} catch (Exception e) {
<("解析BCD(8421码)出错:{}", e.getMessage());
e.printStackTrace();
return defaultVal;
}
}
private int parseIntFromBytes(byte[] data, int startIndex, int length) {
return this.parseIntFromBytes(data, startIndex, length, 0);
}
private int parseIntFromBytes(byte[] data, int startIndex, int length, int defaultVal) {
try {
// 字节数⼤于4,从起始索引开始向后处理4个字节,其余超出部分丢弃
final int len = length > 4 ? 4 : length;
byte[] tmp = new byte[len];
System.arraycopy(data, startIndex, tmp, 0, len);
return bitOperator.byteToInteger(tmp);
} catch (Exception e) {
<("解析整数出错:{}", e.getMessage());
e.printStackTrace();
return defaultVal;
}
}
}
2.2.2 ⽤到的⼯具类
2.2.2.1 BCD操作⼯具类
package cn.hylexus.jt808.util;
public class BCD8421Operater {
/**
* BCD字节数组===>String
*
* @param bytes
* @return ⼗进制字符串
*/
public String bcd2String(byte[] bytes) {
StringBuilder temp = new StringBuilder(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
// ⾼四位
temp.append((bytes[i] & 0xf0) >>> 4);
// 低四位
temp.append(bytes[i] & 0x0f);
}
String().substring(0, 1).equalsIgnoreCase("0") ? String().substring(1) : String();  }
/**
* 字符串==>BCD字节数组
*
* @param str
* @return BCD字节数组
*/
public byte[] string2Bcd(String str) {
// 奇数,前补零
if ((str.length() & 0x1) == 1) {
str = "0" + str;
}
byte ret[] = new byte[str.length() / 2];
byte bs[] = Bytes();
for (int i = 0; i < ret.length; i++) {
byte high = ascII2Bcd(bs[2 * i]);
byte low = ascII2Bcd(bs[2 * i + 1]);
// TODO 只遮罩BCD低四位?
ret[i] = (byte) ((high << 4) | low);
}
return ret;
}
private byte ascII2Bcd(byte asc) {
if ((asc >= '0') && (asc <= '9'))
return (byte) (asc - '0');
else if ((asc >= 'A') && (asc <= 'F'))
return (byte) (asc - 'A' + 10);
else if ((asc >= 'a') && (asc <= 'f'))
return (byte) (asc - 'a' + 10);
else
return (byte) (asc - 48);
}
}
2.2.2.2 位操作⼯具类

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。