• 对于构建自定义协议的思考(Java)


    工作转眼也1年时间了,回顾历程,协议占了绝大多数
    在这里插入图片描述

    JSON(比较常见的通信文本了),protoBuf(小编有写过教程),自定义协议(字节拼接,在一些iot领域中的标准几乎都是字节拼接),当然还有很多其他的但是我不会,还有通过asn完成协议(没接触过)
    对于JSON和protoBuf来说,相对简单,因为有现成的库调用JSON,protoBuf编译器;
    而对于字节拼接的话可能比较复杂,或者说本来不复杂,但是协议复杂,拼接就复杂了;

    而拼接字节形式的协议,又涉及到大端小端模式(大端模式是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端;而小端模式是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端)
    简单说明就是:大端是将一个数直接转成bytes数组;而小端则需要对数组进行reverse反转操作后的数组;

    假设有这样的一个协议:(下面以大端为例子)
    协议头:

    消息名称占用字节数
    消息头1
    消息类型1
    消息版本1
    消息时间戳8
    消息体1+15*n

    协议内容:

    消息名称占用字节数
    感知物体数量1
    感知物体列表15*n

    感知物体对象:

    消息名称占用字节数
    感知物体id1
    感知物速度4
    感知物航向角2
    感知物经度4
    感知物纬度4

    那么我在Java中将构建这样几个对象用来实现协议:
    结构:
    在这里插入图片描述
    实体上层接口规范:Protocol

    public abstract class Protocol {
        byte[] bytes = new byte[0];//抽象出来的bytes数组,用于存储继承者的最终字节
    
        void toBytes() {//抽象出来的方法,约束子类必须实现
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    消息头:

    @Data
    public class ProtocolHead<T extends Protocol> extends Protocol {
        private byte msgHead;
        private byte msgType;
        private byte msgVersion;
        private long timeStamp;
        private T msgBody;
    
        @Override
        public void toBytes() {
            msgBody.toBytes();//将msgBody 字节数组化
            ByteBuffer buffer = ByteBuffer.allocate(1 + 1 + 1 + 8 + msgBody.bytes.length);
            buffer.put(msgHead);
            buffer.put(msgType);
            buffer.put(msgVersion);
            buffer.putLong(timeStamp);
            buffer.put(msgBody.bytes);
            bytes = buffer.array();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    消息体:

    @Data
    public class ProtocolBody extends Protocol {
        private byte perNum;
        private PerceptionData[] perceptionDataArr;
    
        @Override
        public void toBytes() {
            int size = 1;
            for (byte i = 0; i < perNum; i++) {
                perceptionDataArr[i].toBytes();//将每一个感知对象 字节化
                size += perceptionDataArr[i].bytes.length;
            }
            ByteBuffer buffer = ByteBuffer.allocate(size);
            for (byte i = 0; i < perNum; i++) {
                buffer.put(perceptionDataArr[i].bytes);
            }
            bytes=buffer.array();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    消息内容:

    @Data
    public class PerceptionData extends Protocol {
    
        private byte id;
        private int speed;
        private short heading;
        private int lon;
        private int lat;
    
        @Override
        public void toBytes() {
            ByteBuffer buffer = ByteBuffer.allocate(1 + 4 + 2 + 4 + 4);
            buffer.put(id);
            buffer.putInt(speed);
            buffer.putShort(heading);
            buffer.putInt(lon);
            buffer.putInt(lat);
            bytes= buffer.array();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    输入数据(模拟设备传入的数据)

    @Data
    public class InputPerData {
    
        private long timeStamp;
        private List<Per> perList;
    
    
        @Data
       public static class Per {
            private byte id;
            private int speed;
            private short heading;
            private int lon;
            private int lat;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    解析类 convert 转换:

    public class EncodeParser {
    
        public byte[] convertData(InputPerData inputPerData) {
    
            PerceptionData[] perArr = new PerceptionData[inputPerData.getPerList().size()];
            for (int i = 0; i < inputPerData.getPerList().size(); i++) {
                InputPerData.Per inputPer = inputPerData.getPerList().get(i);
                perArr[i] = new PerceptionData();
                perArr[i].setId(inputPer.getId());
                perArr[i].setSpeed(inputPer.getSpeed());
                perArr[i].setHeading(inputPer.getHeading());
                perArr[i].setLon(inputPer.getLon());
                perArr[i].setLat(inputPer.getLat());
            }
    
            ProtocolBody body = new ProtocolBody();
            body.setPerNum((byte) inputPerData.getPerList().size());
            body.setPerceptionDataArr(perArr);
    
    
            ProtocolHead<ProtocolBody> head = new ProtocolHead<>();
            head.setMsgHead((byte) 0x01);
            head.setMsgType((byte) 0x04);
            head.setMsgVersion((byte) 0x01);
            head.setTimeStamp(inputPerData.getTimeStamp());
            head.setMsgBody(body);
    
            head.toBytes();//调用编码方法
    
            return head.bytes;
    
    
        }
    
        public static void main(String[] args) {
            InputPerData.Per per = new InputPerData.Per();
            per.setId((byte) 1);
            per.setSpeed(11);
            per.setHeading((short) 180);
            per.setLon(111342345);
            per.setLat(260099888);
            InputPerData.Per per1 = new InputPerData.Per();
            per1.setId((byte) 2);
            per1.setSpeed(10);
            per1.setHeading((short) 120);
            per1.setLon(114909989);
            per1.setLat(269894903);
    
            EncodeParser parser = new EncodeParser();
            InputPerData input = new InputPerData();
            input.setTimeStamp(System.currentTimeMillis());
            input.setPerList(Lists.newArrayList(per, per1));
            byte[] bytes = parser.convertData(input);//最终byte用于网络通信,常见于 netty的tcp/udp通信
            System.out.println("bytes = " + Arrays.toString(bytes));
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    感觉这样能减少在代码内部的侵入计算,编写自定义协议也更加方便;
    文章有不足的地方还望指正,小子修改
    当然大佬们有什么更好的方法,可以沟通交流一下

  • 相关阅读:
    js-map方法中调用服务器接口
    windows下实现mysql8的主从复制
    Java【方法】,方法重载,方法递归,你都会了吗?
    学信息系统项目管理师第4版系列34_10大管理49过程ITTO
    【JS基础】1.函数、匿名函数、回调函数、IIFE(立即执行函数)
    暑假加餐|有钱人和你想的不一样(第2天)+多目标蝙蝠优化算法(Matlab代码实现)
    vue 中的性能优化
    什么是数据采集与监视控制系统(SCADA)?
    狄兰·托马斯诗合集▷Do not go gentle into that good night
    Linux 基础入门
  • 原文地址:https://blog.csdn.net/an_gentle_killer/article/details/134038110