资讯专栏INFORMATION COLUMN

java解析JT808协议

ZoomQuiet / 2962人阅读

摘要:本篇文章将介绍协议的解析思路。消息体实体类以下是对整个消息体抽象出来的一个实体类。

[TOC]

本篇文章将介绍JT808协议的解析思路。
另请大神绕路,不喜勿喷!
先写个大致的思路,有疑问可以联系本人,联系方式:

emial: hylexus@163.com

1 JT808协议扫盲 1.1 数据类型
数据类型 描述及要求
BYTE 无符号单字节整形(字节, 8 位)
WORD 无符号双字节整形(字, 16 位)
DWORD 无符号四字节整形(双字, 32 位)
BYTE[n] n 字节
BCD[n] 8421 码, n 字节
STRING GBK 编码,若无数据,置空
1.2 消息结构
标识位 消息头 消息体 校验码 标识位
1byte(0x7e) 16byte 1byte 1byte(0x7e)
1.3 消息头

</>复制代码

  1. 消息ID(0-1) 消息体属性(2-3) 终端手机号(4-9) 消息流水号(10-11) 消息包封装项(12-15)
  2. byte[0-1] 消息ID word(16)
  3. byte[2-3] 消息体属性 word(16)
  4. bit[0-9] 消息体长度
  5. bit[10-12] 数据加密方式
  6. 此三位都为 0,表示消息体不加密
  7. 第 10 位为 1,表示消息体经过 RSA 算法加密
  8. 其它保留
  9. bit[13] 分包
  10. 1:消息体卫长消息,进行分包发送处理,具体分包信息由消息包封装项决定
  11. 0:则消息头中无消息包封装项字段
  12. bit[14-15] 保留
  13. byte[4-9] 终端手机号或设备ID bcd[6]
  14. 根据安装后终端自身的手机号转换
  15. 手机号不足12 位,则在前面补 0
  16. byte[10-11] 消息流水号 word(16)
  17. 按发送顺序从 0 开始循环累加
  18. byte[12-15] 消息包封装项
  19. byte[0-1] 消息包总数(word(16))
  20. 该消息分包后得总包数
  21. byte[2-3] 包序号(word(16))
  22. 从 1 开始
  23. 如果消息体属性中相关标识位确定消息分包处理,则该项有内容
  24. 否则无该项
2 解析

整个消息体结构中最复杂的就是消息头了。

2.1 消息体实体类

以下是对整个消息体抽象出来的一个java实体类。

</>复制代码

  1. import java.nio.channels.Channel;
  2. public class PackageData {
  3. /**
  4. * 16byte 消息头
  5. */
  6. protected MsgHeader msgHeader;
  7. // 消息体字节数组
  8. protected byte[] msgBodyBytes;
  9. /**
  10. * 校验码 1byte
  11. */
  12. protected int checkSum;
  13. //记录每个客户端的channel,以便下发信息给客户端
  14. protected Channel channel;
  15. public MsgHeader getMsgHeader() {
  16. return msgHeader;
  17. }
  18. //TODO setget 方法在此处省略
  19. //消息头
  20. public static class MsgHeader {
  21. // 消息ID
  22. protected int msgId;
  23. /////// ========消息体属性
  24. // byte[2-3]
  25. protected int msgBodyPropsField;
  26. // 消息体长度
  27. protected int msgBodyLength;
  28. // 数据加密方式
  29. protected int encryptionType;
  30. // 是否分包,true==>有消息包封装项
  31. protected boolean hasSubPackage;
  32. // 保留位[14-15]
  33. protected String reservedBit;
  34. /////// ========消息体属性
  35. // 终端手机号
  36. protected String terminalPhone;
  37. // 流水号
  38. protected int flowId;
  39. //////// =====消息包封装项
  40. // byte[12-15]
  41. protected int packageInfoField;
  42. // 消息包总数(word(16))
  43. protected long totalSubPackage;
  44. // 包序号(word(16))这次发送的这个消息包是分包中的第几个消息包, 从 1 开始
  45. protected long subPackageSeq;
  46. //////// =====消息包封装项
  47. //TODO setget 方法在此处省略
  48. }
  49. }
2.2 字节数组到消息体实体类的转换 2.2.1 消息转换器

</>复制代码

  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. import cn.hylexus.jt808.util.BCD8421Operater;
  4. import cn.hylexus.jt808.util.BitOperator;
  5. import cn.hylexus.jt808.vo.PackageData;
  6. import cn.hylexus.jt808.vo.PackageData.MsgHeader;
  7. public class MsgDecoder {
  8. private static final Logger log = LoggerFactory.getLogger(MsgDecoder.class);
  9. private BitOperator bitOperator;
  10. private BCD8421Operater bcd8421Operater;
  11. public MsgDecoder() {
  12. this.bitOperator = new BitOperator();
  13. this.bcd8421Operater = new BCD8421Operater();
  14. }
  15. //字节数组到消息体实体类
  16. public PackageData queueElement2PackageData(byte[] data) {
  17. PackageData ret = new PackageData();
  18. // 1. 16byte 或 12byte 消息头
  19. MsgHeader msgHeader = this.parseMsgHeaderFromBytes(data);
  20. ret.setMsgHeader(msgHeader);
  21. int msgBodyByteStartIndex = 12;
  22. // 2. 消息体
  23. // 有子包信息,消息体起始字节后移四个字节:消息包总数(word(16))+包序号(word(16))
  24. if (msgHeader.isHasSubPackage()) {
  25. msgBodyByteStartIndex = 16;
  26. }
  27. byte[] tmp = new byte[msgHeader.getMsgBodyLength()];
  28. System.arraycopy(data, msgBodyByteStartIndex, tmp, 0, tmp.length);
  29. ret.setMsgBodyBytes(tmp);
  30. // 3. 去掉分隔符之后,最后一位就是校验码
  31. // int checkSumInPkg =
  32. // this.bitOperator.oneByteToInteger(data[data.length - 1]);
  33. int checkSumInPkg = data[data.length - 1];
  34. int calculatedCheckSum = this.bitOperator.getCheckSum4JT808(data, 0, data.length - 1);
  35. ret.setCheckSum(checkSumInPkg);
  36. if (checkSumInPkg != calculatedCheckSum) {
  37. log.warn("检验码不一致,msgid:{},pkg:{},calculated:{}", msgHeader.getMsgId(), checkSumInPkg, calculatedCheckSum);
  38. }
  39. return ret;
  40. }
  41. private MsgHeader parseMsgHeaderFromBytes(byte[] data) {
  42. MsgHeader msgHeader = new MsgHeader();
  43. // 1. 消息ID word(16)
  44. // byte[] tmp = new byte[2];
  45. // System.arraycopy(data, 0, tmp, 0, 2);
  46. // msgHeader.setMsgId(this.bitOperator.twoBytesToInteger(tmp));
  47. msgHeader.setMsgId(this.parseIntFromBytes(data, 0, 2));
  48. // 2. 消息体属性 word(16)=================>
  49. // System.arraycopy(data, 2, tmp, 0, 2);
  50. // int msgBodyProps = this.bitOperator.twoBytesToInteger(tmp);
  51. int msgBodyProps = this.parseIntFromBytes(data, 2, 2);
  52. msgHeader.setMsgBodyPropsField(msgBodyProps);
  53. // [ 0-9 ] 0000,0011,1111,1111(3FF)(消息体长度)
  54. msgHeader.setMsgBodyLength(msgBodyProps & 0x1ff);
  55. // [10-12] 0001,1100,0000,0000(1C00)(加密类型)
  56. msgHeader.setEncryptionType((msgBodyProps & 0xe00) >> 10);
  57. // [ 13_ ] 0010,0000,0000,0000(2000)(是否有子包)
  58. msgHeader.setHasSubPackage(((msgBodyProps & 0x2000) >> 13) == 1);
  59. // [14-15] 1100,0000,0000,0000(C000)(保留位)
  60. msgHeader.setReservedBit(((msgBodyProps & 0xc000) >> 14) + "");
  61. // 消息体属性 word(16)<=================
  62. // 3. 终端手机号 bcd[6]
  63. // tmp = new byte[6];
  64. // System.arraycopy(data, 4, tmp, 0, 6);
  65. // msgHeader.setTerminalPhone(this.bcd8421Operater.bcd2String(tmp));
  66. msgHeader.setTerminalPhone(this.parseBcdStringFromBytes(data, 4, 6));
  67. // 4. 消息流水号 word(16) 按发送顺序从 0 开始循环累加
  68. // tmp = new byte[2];
  69. // System.arraycopy(data, 10, tmp, 0, 2);
  70. // msgHeader.setFlowId(this.bitOperator.twoBytesToInteger(tmp));
  71. msgHeader.setFlowId(this.parseIntFromBytes(data, 10, 2));
  72. // 5. 消息包封装项
  73. // 有子包信息
  74. if (msgHeader.isHasSubPackage()) {
  75. // 消息包封装项字段
  76. msgHeader.setPackageInfoField(this.parseIntFromBytes(data, 12, 4));
  77. // byte[0-1] 消息包总数(word(16))
  78. // tmp = new byte[2];
  79. // System.arraycopy(data, 12, tmp, 0, 2);
  80. // msgHeader.setTotalSubPackage(this.bitOperator.twoBytesToInteger(tmp));
  81. msgHeader.setTotalSubPackage(this.parseIntFromBytes(data, 12, 2));
  82. // byte[2-3] 包序号(word(16)) 从 1 开始
  83. // tmp = new byte[2];
  84. // System.arraycopy(data, 14, tmp, 0, 2);
  85. // msgHeader.setSubPackageSeq(this.bitOperator.twoBytesToInteger(tmp));
  86. msgHeader.setSubPackageSeq(this.parseIntFromBytes(data, 12, 2));
  87. }
  88. return msgHeader;
  89. }
  90. protected String parseStringFromBytes(byte[] data, int startIndex, int lenth) {
  91. return this.parseStringFromBytes(data, startIndex, lenth, null);
  92. }
  93. private String parseStringFromBytes(byte[] data, int startIndex, int lenth, String defaultVal) {
  94. try {
  95. byte[] tmp = new byte[lenth];
  96. System.arraycopy(data, startIndex, tmp, 0, lenth);
  97. return new String(tmp, "UTF-8");
  98. } catch (Exception e) {
  99. log.error("解析字符串出错:{}", e.getMessage());
  100. e.printStackTrace();
  101. return defaultVal;
  102. }
  103. }
  104. private String parseBcdStringFromBytes(byte[] data, int startIndex, int lenth) {
  105. return this.parseBcdStringFromBytes(data, startIndex, lenth, null);
  106. }
  107. private String parseBcdStringFromBytes(byte[] data, int startIndex, int lenth, String defaultVal) {
  108. try {
  109. byte[] tmp = new byte[lenth];
  110. System.arraycopy(data, startIndex, tmp, 0, lenth);
  111. return this.bcd8421Operater.bcd2String(tmp);
  112. } catch (Exception e) {
  113. log.error("解析BCD(8421码)出错:{}", e.getMessage());
  114. e.printStackTrace();
  115. return defaultVal;
  116. }
  117. }
  118. private int parseIntFromBytes(byte[] data, int startIndex, int length) {
  119. return this.parseIntFromBytes(data, startIndex, length, 0);
  120. }
  121. private int parseIntFromBytes(byte[] data, int startIndex, int length, int defaultVal) {
  122. try {
  123. // 字节数大于4,从起始索引开始向后处理4个字节,其余超出部分丢弃
  124. final int len = length > 4 ? 4 : length;
  125. byte[] tmp = new byte[len];
  126. System.arraycopy(data, startIndex, tmp, 0, len);
  127. return bitOperator.byteToInteger(tmp);
  128. } catch (Exception e) {
  129. log.error("解析整数出错:{}", e.getMessage());
  130. e.printStackTrace();
  131. return defaultVal;
  132. }
  133. }
  134. }
2.2.2 用到的工具类 2.2.2.1 BCD操作工具类

</>复制代码

  1. package cn.hylexus.jt808.util;
  2. public class BCD8421Operater {
  3. /**
  4. * BCD字节数组===>String
  5. *
  6. * @param bytes
  7. * @return 十进制字符串
  8. */
  9. public String bcd2String(byte[] bytes) {
  10. StringBuilder temp = new StringBuilder(bytes.length * 2);
  11. for (int i = 0; i < bytes.length; i++) {
  12. // 高四位
  13. temp.append((bytes[i] & 0xf0) >>> 4);
  14. // 低四位
  15. temp.append(bytes[i] & 0x0f);
  16. }
  17. return temp.toString().substring(0, 1).equalsIgnoreCase("0") ? temp.toString().substring(1) : temp.toString();
  18. }
  19. /**
  20. * 字符串==>BCD字节数组
  21. *
  22. * @param str
  23. * @return BCD字节数组
  24. */
  25. public byte[] string2Bcd(String str) {
  26. // 奇数,前补零
  27. if ((str.length() & 0x1) == 1) {
  28. str = "0" + str;
  29. }
  30. byte ret[] = new byte[str.length() / 2];
  31. byte bs[] = str.getBytes();
  32. for (int i = 0; i < ret.length; i++) {
  33. byte high = ascII2Bcd(bs[2 * i]);
  34. byte low = ascII2Bcd(bs[2 * i + 1]);
  35. // TODO 只遮罩BCD低四位?
  36. ret[i] = (byte) ((high << 4) | low);
  37. }
  38. return ret;
  39. }
  40. private byte ascII2Bcd(byte asc) {
  41. if ((asc >= "0") && (asc <= "9"))
  42. return (byte) (asc - "0");
  43. else if ((asc >= "A") && (asc <= "F"))
  44. return (byte) (asc - "A" + 10);
  45. else if ((asc >= "a") && (asc <= "f"))
  46. return (byte) (asc - "a" + 10);
  47. else
  48. return (byte) (asc - 48);
  49. }
  50. }
2.2.2.2 位操作工具类

</>复制代码

  1. package cn.hylexus.jt808.util;
  2. import java.util.Arrays;
  3. import java.util.List;
  4. public class BitOperator {
  5. /**
  6. * 把一个整形该为byte
  7. *
  8. * @param value
  9. * @return
  10. * @throws Exception
  11. */
  12. public byte integerTo1Byte(int value) {
  13. return (byte) (value & 0xFF);
  14. }
  15. /**
  16. * 把一个整形该为1位的byte数组
  17. *
  18. * @param value
  19. * @return
  20. * @throws Exception
  21. */
  22. public byte[] integerTo1Bytes(int value) {
  23. byte[] result = new byte[1];
  24. result[0] = (byte) (value & 0xFF);
  25. return result;
  26. }
  27. /**
  28. * 把一个整形改为2位的byte数组
  29. *
  30. * @param value
  31. * @return
  32. * @throws Exception
  33. */
  34. public byte[] integerTo2Bytes(int value) {
  35. byte[] result = new byte[2];
  36. result[0] = (byte) ((value >>> 8) & 0xFF);
  37. result[1] = (byte) (value & 0xFF);
  38. return result;
  39. }
  40. /**
  41. * 把一个整形改为3位的byte数组
  42. *
  43. * @param value
  44. * @return
  45. * @throws Exception
  46. */
  47. public byte[] integerTo3Bytes(int value) {
  48. byte[] result = new byte[3];
  49. result[0] = (byte) ((value >>> 16) & 0xFF);
  50. result[1] = (byte) ((value >>> 8) & 0xFF);
  51. result[2] = (byte) (value & 0xFF);
  52. return result;
  53. }
  54. /**
  55. * 把一个整形改为4位的byte数组
  56. *
  57. * @param value
  58. * @return
  59. * @throws Exception
  60. */
  61. public byte[] integerTo4Bytes(int value){
  62. byte[] result = new byte[4];
  63. result[0] = (byte) ((value >>> 24) & 0xFF);
  64. result[1] = (byte) ((value >>> 16) & 0xFF);
  65. result[2] = (byte) ((value >>> 8) & 0xFF);
  66. result[3] = (byte) (value & 0xFF);
  67. return result;
  68. }
  69. /**
  70. * 把byte[]转化位整形,通常为指令用
  71. *
  72. * @param value
  73. * @return
  74. * @throws Exception
  75. */
  76. public int byteToInteger(byte[] value) {
  77. int result;
  78. if (value.length == 1) {
  79. result = oneByteToInteger(value[0]);
  80. } else if (value.length == 2) {
  81. result = twoBytesToInteger(value);
  82. } else if (value.length == 3) {
  83. result = threeBytesToInteger(value);
  84. } else if (value.length == 4) {
  85. result = fourBytesToInteger(value);
  86. } else {
  87. result = fourBytesToInteger(value);
  88. }
  89. return result;
  90. }
  91. /**
  92. * 把一个byte转化位整形,通常为指令用
  93. *
  94. * @param value
  95. * @return
  96. * @throws Exception
  97. */
  98. public int oneByteToInteger(byte value) {
  99. return (int) value & 0xFF;
  100. }
  101. /**
  102. * 把一个2位的数组转化位整形
  103. *
  104. * @param value
  105. * @return
  106. * @throws Exception
  107. */
  108. public int twoBytesToInteger(byte[] value) {
  109. // if (value.length < 2) {
  110. // throw new Exception("Byte array too short!");
  111. // }
  112. int temp0 = value[0] & 0xFF;
  113. int temp1 = value[1] & 0xFF;
  114. return ((temp0 << 8) + temp1);
  115. }
  116. /**
  117. * 把一个3位的数组转化位整形
  118. *
  119. * @param value
  120. * @return
  121. * @throws Exception
  122. */
  123. public int threeBytesToInteger(byte[] value) {
  124. int temp0 = value[0] & 0xFF;
  125. int temp1 = value[1] & 0xFF;
  126. int temp2 = value[2] & 0xFF;
  127. return ((temp0 << 16) + (temp1 << 8) + temp2);
  128. }
  129. /**
  130. * 把一个4位的数组转化位整形,通常为指令用
  131. *
  132. * @param value
  133. * @return
  134. * @throws Exception
  135. */
  136. public int fourBytesToInteger(byte[] value) {
  137. // if (value.length < 4) {
  138. // throw new Exception("Byte array too short!");
  139. // }
  140. int temp0 = value[0] & 0xFF;
  141. int temp1 = value[1] & 0xFF;
  142. int temp2 = value[2] & 0xFF;
  143. int temp3 = value[3] & 0xFF;
  144. return ((temp0 << 24) + (temp1 << 16) + (temp2 << 8) + temp3);
  145. }
  146. /**
  147. * 把一个4位的数组转化位整形
  148. *
  149. * @param value
  150. * @return
  151. * @throws Exception
  152. */
  153. public long fourBytesToLong(byte[] value) throws Exception {
  154. // if (value.length < 4) {
  155. // throw new Exception("Byte array too short!");
  156. // }
  157. int temp0 = value[0] & 0xFF;
  158. int temp1 = value[1] & 0xFF;
  159. int temp2 = value[2] & 0xFF;
  160. int temp3 = value[3] & 0xFF;
  161. return (((long) temp0 << 24) + (temp1 << 16) + (temp2 << 8) + temp3);
  162. }
  163. /**
  164. * 把一个数组转化长整形
  165. *
  166. * @param value
  167. * @return
  168. * @throws Exception
  169. */
  170. public long bytes2Long(byte[] value) {
  171. long result = 0;
  172. int len = value.length;
  173. int temp;
  174. for (int i = 0; i < len; i++) {
  175. temp = (len - 1 - i) * 8;
  176. if (temp == 0) {
  177. result += (value[i] & 0x0ff);
  178. } else {
  179. result += (value[i] & 0x0ff) << temp;
  180. }
  181. }
  182. return result;
  183. }
  184. /**
  185. * 把一个长整形改为byte数组
  186. *
  187. * @param value
  188. * @return
  189. * @throws Exception
  190. */
  191. public byte[] longToBytes(long value){
  192. return longToBytes(value, 8);
  193. }
  194. /**
  195. * 把一个长整形改为byte数组
  196. *
  197. * @param value
  198. * @return
  199. * @throws Exception
  200. */
  201. public byte[] longToBytes(long value, int len) {
  202. byte[] result = new byte[len];
  203. int temp;
  204. for (int i = 0; i < len; i++) {
  205. temp = (len - 1 - i) * 8;
  206. if (temp == 0) {
  207. result[i] += (value & 0x0ff);
  208. } else {
  209. result[i] += (value >>> temp) & 0x0ff;
  210. }
  211. }
  212. return result;
  213. }
  214. /**
  215. * 得到一个消息ID
  216. *
  217. * @return
  218. * @throws Exception
  219. */
  220. public byte[] generateTransactionID() throws Exception {
  221. byte[] id = new byte[16];
  222. System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 0, 2);
  223. System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 2, 2);
  224. System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 4, 2);
  225. System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 6, 2);
  226. System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 8, 2);
  227. System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 10, 2);
  228. System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 12, 2);
  229. System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 14, 2);
  230. return id;
  231. }
  232. /**
  233. * 把IP拆分位int数组
  234. *
  235. * @param ip
  236. * @return
  237. * @throws Exception
  238. */
  239. public int[] getIntIPValue(String ip) throws Exception {
  240. String[] sip = ip.split("[.]");
  241. // if (sip.length != 4) {
  242. // throw new Exception("error IPAddress");
  243. // }
  244. int[] intIP = { Integer.parseInt(sip[0]), Integer.parseInt(sip[1]), Integer.parseInt(sip[2]),
  245. Integer.parseInt(sip[3]) };
  246. return intIP;
  247. }
  248. /**
  249. * 把byte类型IP地址转化位字符串
  250. *
  251. * @param address
  252. * @return
  253. * @throws Exception
  254. */
  255. public String getStringIPValue(byte[] address) throws Exception {
  256. int first = this.oneByteToInteger(address[0]);
  257. int second = this.oneByteToInteger(address[1]);
  258. int third = this.oneByteToInteger(address[2]);
  259. int fourth = this.oneByteToInteger(address[3]);
  260. return first + "." + second + "." + third + "." + fourth;
  261. }
  262. /**
  263. * 合并字节数组
  264. *
  265. * @param first
  266. * @param rest
  267. * @return
  268. */
  269. public byte[] concatAll(byte[] first, byte[]... rest) {
  270. int totalLength = first.length;
  271. for (byte[] array : rest) {
  272. if (array != null) {
  273. totalLength += array.length;
  274. }
  275. }
  276. byte[] result = Arrays.copyOf(first, totalLength);
  277. int offset = first.length;
  278. for (byte[] array : rest) {
  279. if (array != null) {
  280. System.arraycopy(array, 0, result, offset, array.length);
  281. offset += array.length;
  282. }
  283. }
  284. return result;
  285. }
  286. /**
  287. * 合并字节数组
  288. *
  289. * @param rest
  290. * @return
  291. */
  292. public byte[] concatAll(List rest) {
  293. int totalLength = 0;
  294. for (byte[] array : rest) {
  295. if (array != null) {
  296. totalLength += array.length;
  297. }
  298. }
  299. byte[] result = new byte[totalLength];
  300. int offset = 0;
  301. for (byte[] array : rest) {
  302. if (array != null) {
  303. System.arraycopy(array, 0, result, offset, array.length);
  304. offset += array.length;
  305. }
  306. }
  307. return result;
  308. }
  309. public float byte2Float(byte[] bs) {
  310. return Float.intBitsToFloat(
  311. (((bs[3] & 0xFF) << 24) + ((bs[2] & 0xFF) << 16) + ((bs[1] & 0xFF) << 8) + (bs[0] & 0xFF)));
  312. }
  313. public float byteBE2Float(byte[] bytes) {
  314. int l;
  315. l = bytes[0];
  316. l &= 0xff;
  317. l |= ((long) bytes[1] << 8);
  318. l &= 0xffff;
  319. l |= ((long) bytes[2] << 16);
  320. l &= 0xffffff;
  321. l |= ((long) bytes[3] << 24);
  322. return Float.intBitsToFloat(l);
  323. }
  324. public int getCheckSum4JT808(byte[] bs, int start, int end) {
  325. if (start < 0 || end > bs.length)
  326. throw new ArrayIndexOutOfBoundsException("getCheckSum4JT808 error : index out of bounds(start=" + start
  327. + ",end=" + end + ",bytes length=" + bs.length + ")");
  328. int cs = 0;
  329. for (int i = start; i < end; i++) {
  330. cs ^= bs[i];
  331. }
  332. return cs;
  333. }
  334. public int getBitRange(int number, int start, int end) {
  335. if (start < 0)
  336. throw new IndexOutOfBoundsException("min index is 0,but start = " + start);
  337. if (end >= Integer.SIZE)
  338. throw new IndexOutOfBoundsException("max index is " + (Integer.SIZE - 1) + ",but end = " + end);
  339. return (number << Integer.SIZE - (end + 1)) >>> Integer.SIZE - (end - start + 1);
  340. }
  341. public int getBitAt(int number, int index) {
  342. if (index < 0)
  343. throw new IndexOutOfBoundsException("min index is 0,but " + index);
  344. if (index >= Integer.SIZE)
  345. throw new IndexOutOfBoundsException("max index is " + (Integer.SIZE - 1) + ",but " + index);
  346. return ((1 << index) & number) >> index;
  347. }
  348. public int getBitAtS(int number, int index) {
  349. String s = Integer.toBinaryString(number);
  350. return Integer.parseInt(s.charAt(index) + "");
  351. }
  352. @Deprecated
  353. public int getBitRangeS(int number, int start, int end) {
  354. String s = Integer.toBinaryString(number);
  355. StringBuilder sb = new StringBuilder(s);
  356. while (sb.length() < Integer.SIZE) {
  357. sb.insert(0, "0");
  358. }
  359. String tmp = sb.reverse().substring(start, end + 1);
  360. sb = new StringBuilder(tmp);
  361. return Integer.parseInt(sb.reverse().toString(), 2);
  362. }
  363. }
2.3 和netty结合 2.3.1 netty处理器链

</>复制代码

  1. import java.util.concurrent.TimeUnit;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import cn.kkbc.tpms.tcp.service.TCPServerHandler;
  5. import io.netty.bootstrap.ServerBootstrap;
  6. import io.netty.buffer.Unpooled;
  7. import io.netty.channel.ChannelFuture;
  8. import io.netty.channel.ChannelInitializer;
  9. import io.netty.channel.ChannelOption;
  10. import io.netty.channel.EventLoopGroup;
  11. import io.netty.channel.nio.NioEventLoopGroup;
  12. import io.netty.channel.socket.SocketChannel;
  13. import io.netty.channel.socket.nio.NioServerSocketChannel;
  14. import io.netty.handler.codec.DelimiterBasedFrameDecoder;
  15. import io.netty.handler.timeout.IdleStateHandler;
  16. import io.netty.util.concurrent.Future;
  17. public class TCPServer2 {
  18. private Logger log = LoggerFactory.getLogger(getClass());
  19. private volatile boolean isRunning = false;
  20. private EventLoopGroup bossGroup = null;
  21. private EventLoopGroup workerGroup = null;
  22. private int port;
  23. public TCPServer2() {
  24. }
  25. public TCPServer2(int port) {
  26. this();
  27. this.port = port;
  28. }
  29. private void bind() throws Exception {
  30. this.bossGroup = new NioEventLoopGroup();
  31. this.workerGroup = new NioEventLoopGroup();
  32. ServerBootstrap serverBootstrap = new ServerBootstrap();
  33. serverBootstrap.group(bossGroup, workerGroup)//
  34. .channel(NioServerSocketChannel.class) //
  35. .childHandler(new ChannelInitializer() { //
  36. @Override
  37. public void initChannel(SocketChannel ch) throws Exception {
  38. //超过15分钟未收到客户端消息则自动断开客户端连接
  39. ch.pipeline().addLast("idleStateHandler",
  40. new IdleStateHandler(15, 0, 0, TimeUnit.MINUTES));
  41. //ch.pipeline().addLast(new Decoder4LoggingOnly());
  42. // 1024表示单条消息的最大长度,解码器在查找分隔符的时候,达到该长度还没找到的话会抛异常
  43. ch.pipeline().addLast(
  44. new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer(new byte[] { 0x7e }),
  45. Unpooled.copiedBuffer(new byte[] { 0x7e, 0x7e })));
  46. ch.pipeline().addLast(new TCPServerHandler());
  47. }
  48. }).option(ChannelOption.SO_BACKLOG, 128) //
  49. .childOption(ChannelOption.SO_KEEPALIVE, true);
  50. this.log.info("TCP服务启动完毕,port={}", this.port);
  51. ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
  52. channelFuture.channel().closeFuture().sync();
  53. }
  54. public synchronized void startServer() {
  55. if (this.isRunning) {
  56. throw new IllegalStateException(this.getName() + " is already started .");
  57. }
  58. this.isRunning = true;
  59. new Thread(() -> {
  60. try {
  61. this.bind();
  62. } catch (Exception e) {
  63. this.log.info("TCP服务启动出错:{}", e.getMessage());
  64. e.printStackTrace();
  65. }
  66. }, this.getName()).start();
  67. }
  68. public synchronized void stopServer() {
  69. if (!this.isRunning) {
  70. throw new IllegalStateException(this.getName() + " is not yet started .");
  71. }
  72. this.isRunning = false;
  73. try {
  74. Future future = this.workerGroup.shutdownGracefully().await();
  75. if (!future.isSuccess()) {
  76. log.error("workerGroup 无法正常停止:{}", future.cause());
  77. }
  78. future = this.bossGroup.shutdownGracefully().await();
  79. if (!future.isSuccess()) {
  80. log.error("bossGroup 无法正常停止:{}", future.cause());
  81. }
  82. } catch (InterruptedException e) {
  83. e.printStackTrace();
  84. }
  85. this.log.info("TCP服务已经停止...");
  86. }
  87. private String getName() {
  88. return "TCP-Server";
  89. }
  90. public static void main(String[] args) throws Exception {
  91. TCPServer2 server = new TCPServer2(20048);
  92. server.startServer();
  93. // Thread.sleep(3000);
  94. // server.stopServer();
  95. }
  96. }
2.3.2 netty针对于JT808的消息处理器

</>复制代码

  1. package cn.hylexus.jt808.service;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import cn.hylexus.jt808.server.SessionManager;
  5. import cn.hylexus.jt808.service.codec.MsgDecoder;
  6. import cn.hylexus.jt808.vo.PackageData;
  7. import cn.hylexus.jt808.vo.Session;
  8. import io.netty.buffer.ByteBuf;
  9. import io.netty.channel.ChannelHandlerContext;
  10. import io.netty.channel.ChannelInboundHandlerAdapter;
  11. import io.netty.handler.timeout.IdleState;
  12. import io.netty.handler.timeout.IdleStateEvent;
  13. import io.netty.util.ReferenceCountUtil;
  14. public class TCPServerHandler extends ChannelInboundHandlerAdapter { // (1)
  15. private final Logger logger = LoggerFactory.getLogger(getClass());
  16. // 一个维护客户端连接的类
  17. private final SessionManager sessionManager;
  18. private MsgDecoder decoder = new MsgDecoder();
  19. public TCPServerHandler() {
  20. this.sessionManager = SessionManager.getInstance();
  21. }
  22. @Override
  23. public void channelRead(ChannelHandlerContext ctx, Object msg) throws InterruptedException { // (2)
  24. try {
  25. ByteBuf buf = (ByteBuf) msg;
  26. if (buf.readableBytes() <= 0) {
  27. // ReferenceCountUtil.safeRelease(msg);
  28. return;
  29. }
  30. byte[] bs = new byte[buf.readableBytes()];
  31. buf.readBytes(bs);
  32. PackageData jt808Msg = this.decoder.queueElement2PackageData(bs);
  33. // 处理客户端消息
  34. this.processClientMsg(jt808Msg);
  35. } finally {
  36. release(msg);
  37. }
  38. }
  39. private void processClientMsg(PackageData jt808Msg) {
  40. // TODO 更加消息ID的不同,分别实现自己的业务逻辑
  41. if (jt808Msg.getMsgHeader().getMsgId() == 0x900) {
  42. // TODO ...
  43. } else if (jt808Msg.getMsgHeader().getMsgId() == 0x9001) {
  44. // TODO ...
  45. }
  46. // else if(){}
  47. // else if(){}
  48. // else if(){}
  49. // else if(){}
  50. // ...
  51. else {
  52. logger.error("位置消息,消息ID={}", jt808Msg.getMsgHeader().getMsgId());
  53. }
  54. }
  55. @Override
  56. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
  57. logger.error("发生异常:{}", cause.getMessage());
  58. cause.printStackTrace();
  59. }
  60. @Override
  61. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  62. Session session = Session.buildSession(ctx.channel());
  63. sessionManager.put(session.getId(), session);
  64. logger.debug("终端连接:{}", session);
  65. }
  66. @Override
  67. public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  68. final String sessionId = ctx.channel().id().asLongText();
  69. Session session = sessionManager.findBySessionId(sessionId);
  70. this.sessionManager.removeBySessionId(sessionId);
  71. logger.debug("终端断开连接:{}", session);
  72. ctx.channel().close();
  73. // ctx.close();
  74. }
  75. @Override
  76. public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
  77. if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
  78. IdleStateEvent event = (IdleStateEvent) evt;
  79. if (event.state() == IdleState.READER_IDLE) {
  80. Session session = this.sessionManager.removeBySessionId(Session.buildId(ctx.channel()));
  81. logger.error("服务器主动断开连接:{}", session);
  82. ctx.close();
  83. }
  84. }
  85. }
  86. private void release(Object msg) {
  87. try {
  88. ReferenceCountUtil.release(msg);
  89. } catch (Exception e) {
  90. e.printStackTrace();
  91. }
  92. }
  93. }
2.3.3 用到的其他类

</>复制代码

  1. package cn.hylexus.jt808.server;
  2. import java.util.List;
  3. import java.util.Map;
  4. import java.util.Map.Entry;
  5. import java.util.Set;
  6. import java.util.concurrent.ConcurrentHashMap;
  7. import java.util.function.BiConsumer;
  8. import java.util.stream.Collectors;
  9. import cn.hylexus.jt808.vo.Session;
  10. public class SessionManager {
  11. private static volatile SessionManager instance = null;
  12. // netty生成的sessionID和Session的对应关系
  13. private Map sessionIdMap;
  14. // 终端手机号和netty生成的sessionID的对应关系
  15. private Map phoneMap;
  16. public static SessionManager getInstance() {
  17. if (instance == null) {
  18. synchronized (SessionManager.class) {
  19. if (instance == null) {
  20. instance = new SessionManager();
  21. }
  22. }
  23. }
  24. return instance;
  25. }
  26. public SessionManager() {
  27. this.sessionIdMap = new ConcurrentHashMap<>();
  28. this.phoneMap = new ConcurrentHashMap<>();
  29. }
  30. public boolean containsKey(String sessionId) {
  31. return sessionIdMap.containsKey(sessionId);
  32. }
  33. public boolean containsSession(Session session) {
  34. return sessionIdMap.containsValue(session);
  35. }
  36. public Session findBySessionId(String id) {
  37. return sessionIdMap.get(id);
  38. }
  39. public Session findByTerminalPhone(String phone) {
  40. String sessionId = this.phoneMap.get(phone);
  41. if (sessionId == null)
  42. return null;
  43. return this.findBySessionId(sessionId);
  44. }
  45. public synchronized Session put(String key, Session value) {
  46. if (value.getTerminalPhone() != null && !"".equals(value.getTerminalPhone().trim())) {
  47. this.phoneMap.put(value.getTerminalPhone(), value.getId());
  48. }
  49. return sessionIdMap.put(key, value);
  50. }
  51. public synchronized Session removeBySessionId(String sessionId) {
  52. if (sessionId == null)
  53. return null;
  54. Session session = sessionIdMap.remove(sessionId);
  55. if (session == null)
  56. return null;
  57. if (session.getTerminalPhone() != null)
  58. this.phoneMap.remove(session.getTerminalPhone());
  59. return session;
  60. }
  61. public Set keySet() {
  62. return sessionIdMap.keySet();
  63. }
  64. public void forEach(BiConsumer action) {
  65. sessionIdMap.forEach(action);
  66. }
  67. public Set> entrySet() {
  68. return sessionIdMap.entrySet();
  69. }
  70. public List toList() {
  71. return this.sessionIdMap.entrySet().stream().map(e -> e.getValue()).collect(Collectors.toList());
  72. }
  73. }
3 demo级别java示例

请移步: https://github.com/hylexus/jt...

另请不要吝啬,在GitHub给个star让小可装装逼…………^_^

急急忙忙写的博客,先写个大致的思路,有疑问可以联系本人,联系方式:

emial: hylexus@163.com

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66624.html

相关文章

  • 【2022版】基于部标JT808JT1078车载视频位置监控平台介绍-开源项目

    摘要:是定位协议通讯协议基础协议其他协议基于该协议进行扩展。是转发协议监管协议第三方平台通过向进行数据获取与事件下发。苏标主动安全协议高级驾驶辅助报警驾驶员状态报警胎压监测报警盲区监测报警在触发报警时需要上报附件视频图片文本。 ...

    terro 评论0 收藏1
  • JT/T808协议之:0x0001终端通用应答和0x8001平台通用应答

    摘要:将接收到的消息还原转义后除去消息标识和校验位,按位异或得到的结果就是这条消息的校验码,和校验位比对验证其的一致性。将要发出的消息封装好后出去标示位外,按位异或,得到的校验码放在消息尾部,然后转义。 终端是指obd设备,既车载obd设备。 平台是指上文中说到的通过短信设置的上报IP指向的机器所提供的网关服务。 这两种消息一是终端设备发出的,一是平台发出的,都是通用应答的格式,所谓通用既是...

    April 评论0 收藏0
  • 【学习笔记】用python做些事

    摘要:并返回合理错误提示。如果不在则再输入密码,成功则增加用户信息到文件中,密码进行加密处理。作业增加用户名,密码的合法化判断和错误提示。 课时5:字符串-基础 切片,索引 s = use python do somenthing s[1],s[-1],s[1:3],s[1:6:2],s[1:],s[:-1],s[:] spilt,join,[start:stop:step] 常用方法集...

    wdzgege 评论0 收藏0
  • Java窗口(JFrame)从零开始(6)——单选按钮+复选框

    单选按钮+复选框 单选按钮、复选框是什么这个都知道,不做解释。上代码(自己写着玩的,排班不太好)package jframe;import java.awt.BorderLayout;import java.awt.Container;import java.awt.FlowLayout;import java.awt.event.ActionEvent;import java.awt.event...

    youkede 评论0 收藏0
  • LockSupport中的park与unpark原理

    摘要:的好处在于,在诊断问题的时候能够知道的原因推荐使用带有的操作函数作用用于挂起当前线程,如果许可可用,会立马返回,并消费掉许可。 LockSupport是用来创建locks的基本线程阻塞基元,比如AQS中实现线程挂起的方法,就是park,对应唤醒就是unpark。JDK中有使用的如下 showImg(https://segmentfault.com/img/bVblcXS?w=884&h...

    bigdevil_s 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<