byte 字节、byte数组、二进制、十六进制、各种数据类型 进制的转换、byte转二进制 也就是8个bit位,scoket基础知识…
全称:JTT 808-2011 道路运输车辆卫星定位系统终端通讯协议及数据格式
文档截了下开头的图片如下:
数据类型 | 描述及要求 |
---|---|
BYTE | 无符号单字节整型(字节,8 位) |
WORD | 无符号双字节整型(字,16 位) |
DWORD | 无符号四字节整型(双字,32 位) |
BYTE[n] | n字节 |
BCD[n] | 8421 码,n 字节 |
STRING | GBK 编码,若无数据,置空 |
标识位 | 消息头 | 消息体 | 校验吗 | 标识位 |
---|
采用 0x7E 表示,若校验码、消息头以及消息体中出现 0x7E,则要进行转义处理,转义规则定义如下:
0x7E <————> 0x7D 后紧跟一个 0x02;
0x7D<————> 0x7D 后紧跟一个 0x01。
发送一包内容为 0x30 0x7E 0x08 0x7D 0x55 的数据包
则经过封装如下:0x7E 0x30 7D 0x02 0x08 0x7D 0x01 0x55 0x7E
起始字节 | 字段 | 数据类型 | 描述及要求 |
---|---|---|---|
0 | 消息ID | WORD | |
2 | 消息体属性 | WORD | 消息体属性格式结构图见 下图 |
4 | 终端手机号 | BCD[6] | 根据安装后终端自身的手机号转换 手机号不足 12 位 则在前补充数字, 大陆手机号补充数字 0港澳 台 则根据其区号进行位数补充。 |
10 | 消息流水号 | WORD | 按发送顺序从 0 开始循环累加 |
12 | 消息包封装项 | 如果消息体属性中相关标识位确定消息分包处理, 则该项有内容,否则无该项 |
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
保留 | 分包 | 数据加密方式 | 消息体长度 |
校验码指从消息头开始(也就是消息头和消息体),同后一字节异或,直到校验码前一个字节,占用一个字节。
/**
* BCC 校验算法
*
* @param data
* @return 十六进制
*/
public static String getBCC(byte[] data) {
String ret = "";
byte BCC[] = new byte[1];
for (int i = 0; i < data.length; i++) {
BCC[0] = (byte) (BCC[0] ^ data[i]);
}
String hex = Integer.toHexString(BCC[0] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
ret += hex.toUpperCase();
return ret;
Socket socket = new Socket(IP, PORT);
socket.setSoTimeout(5000);
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
String s = "808服务器: " + IP + ":" + PORT + " 连接成功!";
private void sendMsg(byte[] msg){
OutputStream out = socket.getOutputStream();
out.write(msg);
out.flush();
}
8.5章节
:起始字节 | 字段 | 数据类型 | 描述及要求 |
---|---|---|---|
0 | 省域 ID | WORD | 标示终端安装车辆所在的省域,0 保留,由平台取默 认值。省域 ID 采用 GB/T 2260 中规定的行政区划代 码六位中前两位。 |
2 | 市县域 ID | WORD | 标示终端安装车辆所在的市域和县域,0 保留,由平 台取默认值。市县域 ID 采用 GB/T 2260 中规定的行 政区划代码六位中后四位。 |
4 | 制造商 ID | BYTE[5] | 5 个字节,终端制造商编码 |
9 | 终端型号 | BYTE[20] | 20 个字节,此终端型号由制造商自行定义,位数不 足时,后补“0X00”。 |
29 | 终端 ID | BYTE[7] | 7 个字节,由大写字母和数字组成,此终端 ID 由制 造商自行定义,位数不足时,后补“0X00”。 |
36 | 车牌颜色 | BYTE | 车牌颜色,按照 JT/T415-2006 的 5.4.12。 未上牌时,取值为 0。 |
37 | 车辆标识 | STRING | 车牌颜色为 0 时,表示车辆 VIN; 否则,表示公安交通管理部门颁发的机动车号牌。 |
/**
* 终端注册
* @return
*/
public static byte[] register() {
//省域 ID int转2个字节的bytes
byte[] o = BitOperator.numToByteArray(0, 2);
//省域 市县域 ID int转2个字节的bytes
byte[] t = BitOperator.numToByteArray(0, 2);
//制造商 ID
byte[] th = "TYUAN".getBytes();
//终端型号
byte[] f = "U47DPZAMSWAAHQCQ0000".getBytes();
//终端 ID
byte[] fi = "LMY74DT".getBytes();
//车牌颜色 车辆标识
byte[] s = {0, 0};
//合并多个数组成一个数组
return ByteUtil.byteMergerAll(o, t, th, f, fi, s);
}
/**
* 包装808数据
*
* @param msgId 消息id
* @param phone 终端手机号
* @param msgBody 消息体
* @return
*/
public static byte[] generate808(int msgId, String phone, byte[] msgBody) {
//=========================标识位==================================//
byte[] flag = new byte[]{0x7E};
//=========================消息头==================================//
//[0,1]消息Id
byte[] msgIdb = BitOperator.numToByteArray(msgId, 2);
//[2,3]消息体属性
byte[] msgBodyAttributes = msgBodyAttributes(msgBody.length);
//[4,9]终端手机号 BCD[6](占6位)
byte[] terminalPhone = BCD8421Operater.string2Bcd(phone);
//[10,11]流水号
byte[] flowNum = BitOperator.numToByteArray(0, 2);
//[12]消息包封装项 不分包 就没有
byte[] msgHeader = ByteUtil.byteMergerAll(msgIdb, msgBodyAttributes, terminalPhone, flowNum);
//=========================数据合并(消息头,消息体)=====================//
byte[] bytes = ByteUtil.byteMergerAll(msgHeader, msgBody);
//=========================计算校验码==================================//
String checkCodeHexStr = HexUtil.getBCC(bytes);
byte[] checkCode = HexUtil.hexStringToByte(checkCodeHexStr);
//=========================合并:消息头 消息体 校验码 得到总数据============//
byte[] AllData = ByteUtil.byteMergerAll(bytes, checkCode);
//=========================转义 7E和7D==================================//
// 转成16进制字符串
String hexStr = HexUtil.byte2HexStr(AllData);
// 替换 7E和7D
String replaceHexStr = hexStr.replaceAll(FLAG_7D, " 7D 01")
.replaceAll(FLAG_7E, " 7D 02")
// 最后去除空格
.replaceAll(" ", "");
//替换好后 转回byte[]
byte[] replaceByte = HexUtil.hexStringToByte(replaceHexStr);
//=========================最终传输给服务器的数据==================================//
return ByteUtil.byteMergerAll(flag, replaceByte, flag);
}
这一步便是808的核心了,各种数据格式的转换,拼接…
/**
* 生成消息体属性
*
* @param subpackage [13]是否分包 0:不分包 1:分包
*/
private static byte[] msgBodyAttributes(int msgLength, int subpackage) {
byte[] length = BitOperator.numToByteArray(msgLength, 2);
//[0,9]消息体长度
String msgBodyLength = "" +
//第一个字节最后2bit
+(byte) ((length[0] >> 1) & 0x1) + (byte) ((length[0] >> 0) & 0x1)
//第二个字节8bit
+ (byte) ((length[1] >> 7) & 0x1) + (byte) ((length[1] >> 6) & 0x1)
+ (byte) ((length[1] >> 5) & 0x1) + (byte) ((length[1] >> 4) & 0x1)
+ (byte) ((length[1] >> 3) & 0x1) + (byte) ((length[1] >> 2) & 0x1)
+ (byte) ((length[1] >> 1) & 0x1) + (byte) ((length[1] >> 0) & 0x1);
//[10,12]数据加密方式 0 表示不加密
String encryption = "000";
//[13]分包
String subpackageB = String.valueOf(subpackage);
//[14,15]保留位
String reserve = "00";
String msgAttributes = reserve + subpackageB + encryption + msgBodyLength;
// 消息体属性
int msgBodyAttr = Integer.parseInt(msgAttributes, 2);
return BitOperator.numToByteArray(msgBodyAttr, 2);
}
byte[] register = JTT808.register();
byte[] sendByte = JTT808.generate808(0x0100, "040045503457", register);
sendMsg(sendByte);
鉴权
了,也就是平台根据你注册过来的信息然后会返回鉴权码给你;然后你通过拿到的鉴权码在组装成808数据格式
发送给服务器:见文档8.8章节
:起始字节 | 字段 | 数据类型 | 描述及要求 |
---|---|---|---|
0 | 鉴权码 | STRING | 终端重连后上报鉴权码 |
/**
* 注册成功需要鉴权
*/
public void auth(byte[] bytes) {
byte[] auth = JTT808.generate808(0x0102, "040045503457", bytes);
sendMsg(auth);
}
见文档8.3章节
: /**
* 发送心跳
*/
public void heartPkg() {
byte[] heart = JTT808.generate808(0x0002, "040045503457", new byte[]{});
sendMsg(heart);
}
见文档8.18章节
:位置基本信息 | 位置附加信息项列表 |
---|
/**
* 单独上报经纬度
*
* @param lat 纬度
* @param lng 经度
* @return
*/
public static byte[] reportLatLng(long lat, long lng) {
byte[] alarm = {0, 0, 0, 0};
//32 位二进制 从高到低位
String radix2State = "00000000000000000000000000000010";
//2进制转int 在装4个字节的byte
byte[] state = ByteUtil.int2Bytes(Integer.parseInt(radix2State, 2));
//DWORD经纬度
byte[] latb = ByteUtil.longToDword(lat);
byte[] lngb = ByteUtil.longToDword(lng);
byte[] gaoChen = {0, 0};
byte[] speedb = {0, 0};
byte[] orientation = {0, 0};
//bcd时间
byte[] bcdTime = TimeUtils.getBcdTime();
//位置信息附加项
return ByteUtil.byteMergerAll(alarm, state, latb, lngb, gaoChen, speedb, orientation, bcdTime);
}
/**
* 上传经纬度
*/
public synchronized void uploadLatLng() {
long lat = 22581626;
long lng = 113918790;
byte[] bytes = JTT808.reportLatLng(lat, lng);
byte[] sendByte = JTT808.generate808(0x0200, "040045503457", bytes);
socket.sendMsg(sendByte);
}