netty 809协议

news2024/12/22 23:43:09

netty 809协议

  • 目录
    • 概述
      • 需求:
    • 设计思路
    • 实现思路分析
      • 1.netty 809 协议
      • 2.概念
      • 代码
  • 参考资料和推荐阅读

Survive by day and develop by night.
talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,challenge Survive.
happy for hardess to solve denpendies.

目录

在这里插入图片描述

概述

netty 809协议

需求:

netty 809协议

设计思路

实现思路分析

1.netty 809 协议

2.概念

1.先一个数据头,

消息体内容是: 对应的byte 位的了。

我们来看一下message 包体的内容。看下是如何组件的?

组装的时候套上对应的数据头,数据体,CRC,报文头尾等。
在这里组装对应的信息,配置上对应的报文头,数据体,CRC,报文尾就可以发送了。

内部传输的是十六进制2二进制数据。

判断的时候是根据十六进制判断。

转成2进制,对byte 位进行判断。 打包发送。

这个vo 貌似就没有用到。
将重要信息转成二进制数据即可。
组装这个messsageBody.会将vo 去掉。

这是因为java中的byte是有符号位的byte,
这点和c++不一样,因此可表示的数据为-127~127(最高位为符号位)。
知道了原因,剩下的就是问题的解决了。

服务端:
1.服务端接收以后,在计算机中bytes 传送的,首先将接收到数据包转成对应的16进制,
2.检测是有否转义含义
3.最后解析部分数据:

代码

package cn.com.onlinetool.jt809.decoder;


import cn.com.onlinetool.jt809.constants.JT809MessageConstants;
import cn.com.onlinetool.jt809.decoderDemo.JT809Packet0x1202Decoder;
import cn.com.onlinetool.jt809.util.ByteArrayUtil;
import cn.com.onlinetool.jt809.util.PacketUtil;
import cn.com.onlinetool.jt809.util.ParseUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * @author choice
 * @description: 消息解码
 * @date 2018-12-27 14:39
 */
@Slf4j
@Service
public class Byte2MessageDecoder {
    private static Map<String, byte[]> cache = new HashMap<String, byte[]>();

    public void decode(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        String channelKey = ctx.channel().remoteAddress().toString();

        //判断是否有可读的字节
        if (msg.readableBytes() <= 0) {
            return;
        }

        //读取缓冲区数据
        byte[] readDatas = new byte[msg.readableBytes()];
        msg.readBytes(readDatas);
        log.info("接收到数据包, packetLen : {}, packet : {}", readDatas.length, ByteArrayUtil.bytes2HexStr(readDatas));

        //拼接缓存数据
        byte[] cacheDatas = cache.get(channelKey);
        if (null != cacheDatas) {
            readDatas = ByteArrayUtil.append(cacheDatas, readDatas);
            cache.remove(channelKey);
            log.info("拼接后的数据包:{}", ByteArrayUtil.bytes2HexStr(readDatas));
        }

        //校验数据头
//        if (!PacketUtil.checkHeadFlag(readDatas)) {
//            //防止极端情况,请求头校验不通过的情况 丢掉本包数据 同时 丢掉上一包缓存的剩余数据
//            //防止上一包剩余数据不包含数据起始符 会导致拼接后的数据包一直校验不通过
//            cache.remove(channelKey);
//            log.warn("数据包标识符验证失败 : {}", ByteArrayUtil.bytes2HexStr(readDatas));
//            return;
//        }

        //数据转义
        String dataStr = ByteArrayUtil.bytes2FullHexStr(readDatas);
        dataStr = dataStr.replaceAll("0x5a0x01", "0x5b");
        dataStr = dataStr.replaceAll("0x5a0x02", "0x5a");
        dataStr = dataStr.replaceAll("0x5e0x01", "0x5d");
        dataStr = dataStr.replaceAll("0x5e0x02", "0x5e");
        readDatas = ByteArrayUtil.fullHexStr2Bytes(dataStr);
        log.info("转义后的数据包:{}", ByteArrayUtil.bytes2HexStr(readDatas));

        //如果数据小于一整包数据的最小长度
//        if (readDatas.length < JT809MessageConstants.MSG_MIN_LEN) {
//            log.warn("数据长度小于整包数据最小长度,缓存数据:{}", ByteArrayUtil.bytes2HexStr(readDatas));
//            cache.put(channelKey, readDatas);
//            return;
//        }
//
//        //判断是否有完整数据包,没有直接缓存
//        int packetLen = PacketUtil.getPacketLen(readDatas);
//        if (readDatas.length < packetLen) {
//            log.warn("数据长度小于整包数据长度,缓存数据:{}", ByteArrayUtil.bytes2HexStr(readDatas));
//            cache.put(channelKey, readDatas);
//            return;
//        }

        //解析数据
        this.parseAndPushData(ctx, channelKey, 0, readDatas);

    }


    /**
     * 解析并返回数据
     *
     * @param channelKey
     * @param readDatas
     */
    private void parseAndPushData(ChannelHandlerContext ctx, String channelKey, int index, byte[] readDatas) throws Exception {

        byte[] data = ByteArrayUtil.subBytes(readDatas, index, readDatas.length - index);

//        //整包长度
//        int packetLen = PacketUtil.getPacketLen(data);
//        index += packetLen;
//
//        //一个完整包
//        byte[] fullPacket = ByteArrayUtil.subBytes(data, 0, packetLen);
//        log.info("拆包后的单包数据 --> fullPacket : {}", ByteArrayUtil.bytes2HexStr(fullPacket));

        log.info("拆包后的单包数据 --> fullPacket : {}", ByteArrayUtil.bytes2HexStr(data));

//        //验证数据包有效性
//        if (!PacketUtil.checkPacket(fullPacket)) {
//            cache.remove(channelKey);
//            log.info("数据校验失败 --> fullPacket : {}", ByteArrayUtil.bytes2HexStr(fullPacket));
//            return;
//        }
//        ctx.fireChannelRead(PacketUtil.bytes2Message(fullPacket));
//
//        //剩余长度
//        int remainingLen = data.length - packetLen;

        //没有数据,结束
//        if (remainingLen < 1) {
//            return;
//        }
//
//        //剩余数据长度小于一包数据的最小长度,缓存数据
//        if (remainingLen < JT809MessageConstants.MSG_MIN_LEN) {
//            log.warn("剩余数据长度小于整包数据最小长度,缓存数据:{}", ByteArrayUtil.bytes2HexStr(ByteArrayUtil.subBytes(data, data.length - remainingLen, remainingLen)));
//            cache.put(channelKey, ByteArrayUtil.subBytes(data, data.length - remainingLen, remainingLen));
//            return;
//        }
//
//        //下一包数据的总长度
//        packetLen = PacketUtil.getPacketLen(ByteArrayUtil.subBytes(data,data.length - remainingLen, remainingLen));
//        //剩余数据长度小于整包数据长度
//        if (remainingLen < packetLen) {
//            log.warn("剩余数据长度小于整包数据长度,缓存数据:{}", ByteArrayUtil.bytes2HexStr(ByteArrayUtil.subBytes(data, data.length - remainingLen, remainingLen)));
//            cache.put(channelKey, ByteArrayUtil.subBytes(data, data.length - remainingLen, remainingLen));
//            return;
//        }

        //还有完整数据包 递归调用
//        this.parseAndPushData(ctx, channelKey, index, readDatas);


        JT809Packet0x1202Decoder jt809Packet0x1202Decoder=new JT809Packet0x1202Decoder();
        jt809Packet0x1202Decoder.decoder(data);
//        parsePkt(data);



    }

    private void parsePkt(byte[] data) {
        // begin 这里提供方法可供入库使用,根据不同的业务进行字段分段截取.默认解析字段分别是业务字段的含义字段

        String parseData = ByteArrayUtil.bytes2HexStr(data);

        //比如这里增加子业务类型的字段数据:
        if(StringUtil.isNullOrEmpty(parseData)){
             log.info("数据为空");
             return;
        }else {
            //数据头
            String  head = parseData.substring(0, 2); //--头标识
            String  datalength=parseData.substring(2,10);//--数据头->数据长度
            String  dataSeqNo=parseData.substring(10,18);// --数据头->报文序列号
            String  bizdata=parseData.substring(18,22);// --数据头->业务数据类型
            String  code=parseData.substring(22,30); //--数据头->下级平台接入码,上级平台给下级平台分配唯一标识码
            String version=parseData.substring(30,36); //--数据头->协议版本号标识
            String entryFlag=parseData.substring(36,38);//--数据头->报文加密标识位
            String key=parseData.substring(38,46);//--数据头->数据加密的密匙

            //数据体
//            String chepaiHao=parseData.substring(46,50);// --数据体->车牌号
//            String color=parseData.substring(50,52); // --数据体->车辆颜色   //这2者没有
            String biz =parseData.substring(46,50); //--数据体->子业务类型标识
            String lastlength=parseData.substring(50,58);//--数据体->后续数据长度,这里的确是24 位 ,数据长度24位

            //子数据体
            String subData=parseData.substring(58,82); //参看4.5.8.1 车辆定位信息数据体

            //3.CRC
            String crc=parseData.substring(82,86);  //这个每次都变化的
            String tail=parseData.substring(86,88);





            //2.业务数据体这里调用Parse方法,可以封装对应的实体bean,供入库用,提供int ,varchar,time 三种格式进行
//            log.info("总长度是:{}"+subData);









        }


        //end
    }
}

解包代码:

package cn.com.onlinetool.jt809.decoderDemo;


import cn.com.onlinetool.jt809.jt.Decoder;
import io.netty.buffer.ByteBuf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.time.LocalDate;
import java.time.LocalTime;



/**
 * @Author: Xiuming Lee
 * @Date: 2019/9/23 15:21
 * @Version 1.0
 * @Describe:
 */
public class JT809Packet0x1202Decoder {
    private static Logger log = LoggerFactory.getLogger(JT809Packet0x1202Decoder.class);

    public JT809BasePacket decoder(byte[] bytes) throws Exception {
        JT809Packet0x1202 jt809Packet0x1202 = new JT809Packet0x1202();
        ByteBuf byteBuf = PacketDecoderUtils.baseDecoder(bytes, jt809Packet0x1202);
        packetDecoder(byteBuf,jt809Packet0x1202);
        return jt809Packet0x1202;
    }

    private void packetDecoder(ByteBuf byteBuf, JT809Packet0x1202 packet) throws Exception{
        ByteBuf msgBodyBuf = null;
        if (packet.getEncryptFlag() == Const.EncryptFlag.NO) {
            msgBodyBuf = PacketDecoderUtils.getMsgBodyBuf(byteBuf);
        } else {

            msgBodyBuf = null;
            return;
        }
        // 车牌号
        byte [] vehicleNoBytes = new byte[21];
        msgBodyBuf.readBytes(vehicleNoBytes);
        packet.setVehicleNo(new String(vehicleNoBytes, Charset.forName("utf-8")));
        // 车辆颜色
        packet.setVehicleColor(msgBodyBuf.readByte());
        // 子业务类型标识
        packet.setDataType(msgBodyBuf.readShort());
        // 如果不是定位的数据,抛出空指针错误,解码适配器会对空指针错误做处理。
//        if (packet.getDataType() != Const.SubBusinessDataType.UP_EXG_MSG_REAL_LOCATION ) {
//            throw new NullPointerException();
//        }
        // 后续数据长度
        packet.setDataLength(msgBodyBuf.readInt());



        // 经纬度信息是否按国标进行加密
        packet.setExcrypt(msgBodyBuf.readByte());
        if (packet.getExcrypt() == Const.EncryptFlag.YES ){

        }
        // 跳过时间
//        msgBodyBuf.skipBytes(7);
        int day = Byte.toUnsignedInt(msgBodyBuf.readByte());
        int month = Byte.toUnsignedInt(msgBodyBuf.readByte());
//        packet.setDate(LocalDate.of(msgBodyBuf.readShort(),month,day));
//        packet.setTime(LocalTime.of(Byte.toUnsignedInt(msgBodyBuf.readByte()),Byte.toUnsignedInt(msgBodyBuf.readByte()),Byte.toUnsignedInt(msgBodyBuf.readByte())));
        // 经纬度
        packet.setLon(msgBodyBuf.readInt());
        packet.setLat(msgBodyBuf.readInt());
        // 速度
        packet.setVec1(msgBodyBuf.readShort());
        // 行驶记录速度
        packet.setVec2(msgBodyBuf.readShort());
        // 车辆当前总里程数
//        packet.setVec3(msgBodyBuf.readInt());
//        // 方向
//        packet.setDirection(msgBodyBuf.readShort());
//        // 海拔
//        packet.setAltitude(msgBodyBuf.readShort());
//        // 车辆状态
//        packet.setState(msgBodyBuf.readInt());
//        // 报警状态
//        packet.setAlarm(msgBodyBuf.readInt());
    }



}

参考资料和推荐阅读

  1. 暂无

欢迎阅读,各位老铁,如果对你有帮助,点个赞加个关注呗!同时,期望各位大佬的批评指正~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/801168.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ad+硬件每日学习十个知识点(15)23.7.26 (逻辑器件、拉电流和灌电流、OC门和OD门、总线保持)

文章目录 1.逻辑器件介绍2.什么是总线缓冲器&#xff1f;3.逻辑器件按功能分类4.逻辑器件按工艺分类&#xff08;目前主要用CMOS逻辑&#xff09;5.什么是总线保持&#xff1f;6.逻辑电平7.两个逻辑器件互连&#xff0c;需要满足什么要求&#xff1f;8.什么是推挽&#xff0c;什…

关于自签名证书授权后在哪儿看

目录 firefox可以看到 chrome and edge firefox可以看到 chrome and edge 只能从打开的网站左上角进入

读《全球科技通史》总结——历史总在重演,科技永远向前

今天和大家分享一下吴军老师的《全球科技通史》。大部分人谈到历史的时候&#xff0c;关注的是国家的兴衰、王朝的更替&#xff0c;往往忽视了科技的力量。“文津图书奖”得主吴军博士&#xff0c;从科技视角串联历史&#xff0c;首次以能量和信息两条主线&#xff0c;系统阐述…

【Docker】Docker相关基础命令

目录 一、Docker服务相关命令 1、启动docker服务 2、停止docker服务 3、重启docker服务 4、查看docker服务状态 5、开机自启动docker服务 二、Images镜像相关命令 1、查看镜像 2、拉取镜像 3、搜索镜像 4、删除镜像 三、Container容器相关命令 1、创建容器 2、查…

ad+硬件每日学习十个知识点(9)23.7.20

文章目录 1.正点原子fpga开拓者无gui检查项目2.排针连接器A2541WR-XP-2P3.肖特基二极管反接在LDO的输出端&#xff0c;是什么用&#xff1f;4.在AD中如何实现批量元器件的移动&#xff1f;5.在PCB中&#xff0c;如何让元器件以任意角度旋转&#xff1f;6.接口设计都要做静电防护…

MIT学生揭示ChatGPT完成本科课程存在问题

一个机器学习工程师发推特说&#xff1a;“这太疯狂了&#xff01;”一家专注于人工智能新闻的账号称这是一项“开创性的研究”。这项研究声称&#xff0c;ChatGPT能够以100%的准确率完成麻省理工学院的数学、计算机科学和电气工程本科课程。 它每个问题都答对了。 然而&#…

Spring:泛型依赖注入

Spring 泛型依赖注入问题解决 泛型依赖注入 泛型&#xff1a;具有占位符&#xff08;类型参数&#xff09;的类、结构、接口和方法&#xff0c;通过 <> 的方式定义了一个形式参数&#xff0c;在实例化时再指明具体类型 依赖注入&#xff1a;IoC 的具体实现&#xff0c;…

提升性能的利器:理解线程池的使用、工作原理和优势

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览2.1 为什么创建和销毁线程开销较大2.2 为什么要使…

【C++从0到王者】第十三站:vector源码分析及手把手教你如何写一个简单的vector

文章目录 一、vector的源码分析1.分析思路2.构造函数和插入接口 二、手把手教你写一个简单的vector1.基本结构2.迭代器与私有成员变量的定义3.构造函数4.size和capacity5.迭代器函数接口6.析构函数7.reserve接口8.尾插9.operator[]运算符重载10.简单的测试前面的接口11.insert以…

【Spring框架】Spring读取与存储综合练习

练习 在 Spring 项⽬中&#xff0c;通过 main ⽅法获取到 Controller 类&#xff0c;调⽤ Controller ⾥⾯通过注⼊的⽅式调⽤ Service 类&#xff0c;Service 再通过注⼊的⽅式获取到 Repository 类&#xff0c;Repository 类⾥⾯有⼀个⽅法构建⼀个 User 对象&#xff0c;返…

力扣 7月27日每日一题 2500. 删除每行中的最大值

题目连接 》2500. 删除每行中的最大值 有一句话这么说的&#xff1a;不用把人生的每一个目标都定在年龄线上&#xff0c;人生的本质是体验&#xff0c;感悟&#xff0c;和成长。什么年龄该干什么事本来就是一个伪命题。事实上&#xff0c;当你找到你热爱的东西&#xff0c;你…

C++之std::function总结(一百六十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

pycharm 远程连接服务器并且debug, 支持torch.distributed.launch debug

未经允许&#xff0c;本文不得转载&#xff0c;vx&#xff1a;837007389 文章目录 step1&#xff1a;下载专业版本的pycharmstep2 配置自动同步文件夹&#xff0c;即远程的工程文件和本地同步2.1 Tools -> Deployment -> configuration2.2 设置同步文件夹2.3 同步服务器…

【Docker】初识Docker以及Docker安装与阿里云镜像配置

目录 一、初识Docker 二、安装Docker 三、Docker架构 四、配置Docker镜像加速器 一、初识Docker Docker是一个开源的应用容器引擎&#xff0c;诞生于2013年&#xff0c;基于Go语言实现&#xff0c;dotCloud公司出品&#xff0c;Docker开源让开发者打包他们的应用以及依赖包到…

SpringMVC-mybatis,SQL语句中误用了desc关键字,导致报错。

17-Jul-2023 21:26:22.295 淇℃伅 [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.core.ApplicationContext.log 1 Spring WebApplicationInitializers detected on classpath 17-Jul-2023 21:26:22.621 淇℃伅 [RMI TCP Connection(2)-127.0.0.1] org.apache.catalin…

TOP命令参数详解

非常详细的top命令介绍&#xff0c;感谢网友分享&#xff0c;参考地址如下 1、TOP命令参数详解---10分钟学会top用法 - 知乎 摘录几个之前不常用的&#xff0c;可能会用到的命令&#xff1a; 1、MEM vs SWAP 第四第五行分别是内存信息和swap信息&#xff0c;所有程序的运行都…

计算机网络网--应用层

目录 应用层概述1.进程通信2. 供应用程序使用的运输服务3.因特网提供的运输服务 一.网络应用模型1.1 C/S模型&#xff08;client/server&#xff0c;客户/服务器模型&#xff09;定义工作原理功能特点优势应用 1.2 P2P&#xff08;peer to peer&#xff09;模型 与 P2P文件分发…

Golang安装

目录 Go安装下载安装Go Go安装 下载安装Go 地址&#xff1a;https://studygolang.com/dl 1、根据系统来选择下载包。 2、我是Window&#xff0c;所以直接下载windows的安装包来安装。 3、在控制台窗口输入“go version”可查看Go版本&#xff0c;检测是否安装成功。 4、配置…

浅谈es5如何保证并发请求的返回顺序

最近在公司实习写的是es5&#xff0c;在和回调地狱经过一番拉扯之后写下这篇文章&#xff0c;也算是体验了一把没有promise的时代 假设我们的div有一个日历列表&#xff0c;但是由于大小关系只能每次显示2天的信息&#xff0c;项目限制只能使用es5&#xff0c;不能使用es6的pro…

WMS是什么意思,WMS有什么功能

阅读本篇文章&#xff0c;您可以了解到&#xff1a;1、WMS的定义&#xff1b;2、WMS的功能&#xff1b;3、WMS的好处&#xff1b;4、WMS的未来。 一、WMS的定义 WMS全称为Warehouse Management System&#xff0c;即仓库管理系统&#xff0c;是一种用于管理和控制仓库操作的软…