1. 协议结构
2. 协议说明
消息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 开始
如果消息体属性中相关标识位确定消息分包处理,则该项有内容
否则无该项
3. 协议类
public class Jt808Message extends MinaMessage {
public static final byte PREFIX = 0x7E;
public static final byte UNESCAPE = 0x7D;
private int messageId;
private int attributes;
private int packageLength;
private int entryType;
private boolean childPackage;
private int reservedBit;
private String phone;
private int serialNo;
private long packageTotal;
private long packageNumber;
private byte[] bodyBytes;
private byte[] childBytes;
private byte xor;
public Jt808Message(byte[] bytes) {
super.setPacketDescribe(bytes);
bytes = unEscape(bytes);
validateXor(bytes);
int index = 0;
if (bytes[0] == PREFIX) {
index++;
}
this.messageId = BitOperator.parseIntFromBytes(bytes, index, 2);
String idFormat = String.format("[%04x] 消息id:%d", messageId, messageId);
index += 2;
this.attributes = BitOperator.parseIntFromBytes(bytes, index, 2);
this.packageLength = attributes & 0x1ff;
this.entryType = (attributes & 0xe00) >> 10;
this.childPackage = (attributes & 0x2000) >> 13 == 1;
this.reservedBit = (attributes & 0xc000) >> 14;
index += 2;
String attributesFormat = String.format("[%04x] 终端消息体属性,[%x] 消息长度:%d,[%03x] 是否加密:%d,[%x] 是否有子包:%b",
attributes,
packageLength, packageLength, entryType, entryType, attributes & 0x2000, childPackage);
this.phone = readPhoneForBytes(bytes, index, 6);
String phoneFormat = String.format("[%s] 手机号:%s", BitOperator.bytesToHexString(BitOperator.rangeBytes(bytes, index, 6)), phone);
index += 6;
this.serialNo = BitOperator.parseIntFromBytes(bytes, index, 2);
index += 2;
String serialNoFormat = String.format("[%02x] 流水号:%s", serialNo, serialNo);
if (childPackage) {
this.packageTotal = BitOperator.parseIntFromBytes(bytes, index, 2);
index += 2;
this.packageNumber = BitOperator.parseIntFromBytes(bytes, index, 2);
index += 2;
this.childBytes = new byte[packageLength];
System.arraycopy(bytes, index, this.childBytes, 0, packageLength);
} else {
this.bodyBytes = new byte[packageLength];
System.arraycopy(bytes, index, this.bodyBytes, 0, packageLength);
}
index += packageLength;
this.xor = bytes[index];
String xorFormat = String.format("[%02x] 校检码:%d", xor, xor);
index++;
if (bytes[index] != PREFIX) {
throw new IllegalArgumentException("数据格式异常");
}
log.info("数据包16进制:{}", getPacketDescribe());
log.info("{}", idFormat);
log.info("{}", attributesFormat);
log.info("{}", phoneFormat);
log.info("{}", serialNoFormat);
log.info("{}", xorFormat);
}
private byte[] unEscape(byte[] data) {
IoBuffer ioBuffer = IoBuffer.allocate(data.length);
for (int i = 0; i < data.length; i++) {
if (data[i] == UNESCAPE) {
byte datum = data[i + 1];
if (datum == 0x01) {
ioBuffer.put(UNESCAPE);
i++;
} else if (datum == 0x02) {
ioBuffer.put(PREFIX);
i++;
} else {
}
} else {
ioBuffer.put(data[i]);
}
}
return ioBuffer.array();
}
private String readPhoneForBytes(byte[] bytes, int startIndex, int length) {
byte[] tmp = new byte[length];
System.arraycopy(bytes, startIndex, tmp, 0, length);
return BitOperator.bcd2String(tmp);
}
public void validateXor(byte[] bytes) {
int index = 0;
int endIndex = 0;
if (bytes[0] == PREFIX) {
index++;
}
if (bytes[bytes.length - 1] == PREFIX) {
endIndex = bytes.length - 2;
}
byte xor = BitOperator.getCheckSum4JT808(bytes, index, endIndex);
byte packetXor = bytes[endIndex];
if (xor != packetXor) {
throw new IllegalArgumentException(String.format("packet校检码错误,包传递xor:%d,实际解析xor:%d", packetXor, xor));
}
}
}
4. 定义抽象类
public abstract class MinaMessage {
private String packetDescribe;
public String getPacketDescribe() {
return packetDescribe;
}
public void setPacketDescribe(byte[] bytes) {
this.packetDescribe = BitOperator.bytesToHexString(bytes);
}
}
5. 工具类
public class BitOperator {
public static final String DIGITAL = "0123456789ABCDEF";
public static int byteToInteger(byte[] value) {
int result;
if (value.length == 1) {
result = oneByteToInteger(value[0]);
} else if (value.length == 2) {
result = twoBytesToInteger(value);
} else if (value.length == 3) {
result = threeBytesToInteger(value);
} else if (value.length == 4) {
result = fourBytesToInteger(value);
} else {
result = fourBytesToInteger(value);
}
return result;
}
private static int oneByteToInteger(byte value) {
return (int) value & 0xFF;
}
private static int twoBytesToInteger(byte[] value) {
int temp0 = value[0] & 0xFF;
int temp1 = value[1] & 0xFF;
return ((temp0 << 8) + temp1);
}
private static int threeBytesToInteger(byte[] value) {
int temp0 = value[0] & 0xFF;
int temp1 = value[1] & 0xFF;
int temp2 = value[2] & 0xFF;
return ((temp0 << 16) + (temp1 << 8) + temp2);
}
private static int fourBytesToInteger(byte[] value) {
int temp0 = value[0] & 0xFF;
int temp1 = value[1] & 0xFF;
int temp2 = value[2] & 0xFF;
int temp3 = value[3] & 0xFF;
return ((temp0 << 24) + (temp1 << 16) + (temp2 << 8) + temp3);
}
public static byte getCheckSum4JT808(byte[] bs, int start, int end) {
if (start < 0 || end > bs.length) {
throw new ArrayIndexOutOfBoundsException("getCheckSum4JT808 error : index out of bounds(start=" + start
+ ",end=" + end + ",bytes length=" + bs.length + ")");
}
byte cs = 0;
for (int i = start; i < end; i++) {
cs ^= bs[i];
}
return cs;
}
public static 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);
}
return temp.substring(0, 1).equalsIgnoreCase("0") ? temp.substring(1) : temp.toString();
}
public static int parseIntFromBytes(byte[] data, int startIndex, int length) {
final int len = Math.min(length, 4);
byte[] tmp = new byte[len];
System.arraycopy(data, startIndex, tmp, 0, len);
return byteToInteger(tmp);
}
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder();
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
public static String intToHexString(int data, int byteNum) {
StringBuilder sb = new StringBuilder();
for (int m = 0; m < byteNum; m++) {
sb.append("00");
}
int totalLen = byteNum * 2;
String tmp = Integer.toHexString(data);
sb.replace(totalLen - tmp.length(), totalLen, tmp);
return sb.toString();
}
public static byte[] stringToBytes(String hex) {
String hex1 = hex.replace(" ", "");
char[] hex2char = hex1.toCharArray();
byte[] bytes = new byte[hex1.length() / 2];
byte temp;
for (int p = 0; p < bytes.length; p++) {
temp = (byte) (DIGITAL.indexOf(hex2char[2 * p]) * 16);
temp += DIGITAL.indexOf(hex2char[2 * p + 1]);
bytes[p] = (byte) (temp & 0xff);
}
return bytes;
}
public static byte[] rangeBytes(byte[] data, int start, int length) {
byte[] bytes = new byte[length];
System.arraycopy(data, start, bytes, 0, length);
return bytes;
}
}
7. Mina框架解析
CumulativeProtocolDecoder 类提供了TCP粘包拆包的功能实现
public class Jt808ProtocolDecoder extends CumulativeProtocolDecoder {
private static final byte TAG = 0X7E;
@Override
protected boolean doDecode(IoSession ioSession, IoBuffer ioBuffer, ProtocolDecoderOutput protocolDecoderOutput) {
if (ioBuffer.remaining() < 1) {
return false;
}
while (ioBuffer.remaining() > 0) {
ioBuffer.mark();
byte tag = ioBuffer.get();
if (tag == TAG && ioBuffer.remaining() > 0) {
tag = ioBuffer.get();
while (tag != TAG) {
if (ioBuffer.remaining() <= 0) {
ioBuffer.reset();
return false;
}
tag = ioBuffer.get();
}
int position = ioBuffer.position();
int packetSize = position - ioBuffer.markValue();
if (packetSize > 2) {
byte[] data = new byte[packetSize];
ioBuffer.reset();
ioBuffer.get(data);
Jt808Message jt808Message = new Jt808Message(data);
protocolDecoderOutput.write(jt808Message);
} else {
ioBuffer.reset();
ioBuffer.get();
}
}
}
return false;
}
}
8. 参考文档
https://blog.csdn.net/NiceSzy/article/details/120741750
|