netty 809协议

该文章详细介绍了Netty809协议的解码过程,包括接收到数据包后如何进行16进制转换、数据转义、数据校验以及数据解析。在服务端,数据包首先被转换成16进制,然后进行转义处理,接着检查数据包的有效性并进行解析。代码示例展示了如何处理接收到的ByteBuf数据,并进行解码操作。
摘要由CSDN通过智能技术生成

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. 暂无

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

执于代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值