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

工作转眼也1年时间了,回顾历程,协议占了绝大多数

JSON(比较常见的通信文本了),protoBuf(小编有写过教程),自定义协议(字节拼接,在一些iot领域中的标准几乎都是字节拼接),当然还有很多其他的但是我不会,还有通过asn完成协议(没接触过)

对于JSON和protoBuf来说,相对简单,因为有现成的库调用JSON,protoBuf编译器;

而对于字节拼接的话可能比较复杂,或者说本来不复杂,但是协议复杂,拼接就复杂了;

而拼接字节形式的协议,又涉及到大端小端模式(大端模式是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端;而小端模式是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端)

简单说明就是:大端是将一个数直接转成bytes数组;而小端则需要对数组进行reverse反转操作后的数组;

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

协议头:

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

协议内容:

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

感知物体对象:

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

那么我在Java中将构建这样几个对象用来实现协议:

结构:

实体上层接口规范:Protocol

java 复制代码
public abstract class Protocol {
    byte[] bytes = new byte[0];//抽象出来的bytes数组,用于存储继承者的最终字节

    void toBytes() {//抽象出来的方法,约束子类必须实现

    }
}

消息头:

java 复制代码
@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();
    }
}

消息体:

java 复制代码
@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();
    }
}

消息内容:

java 复制代码
@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();
    }
}

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

java 复制代码
@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;
    }
}

解析类 convert 转换:

java 复制代码
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));
    }

}

感觉这样能减少在代码内部的侵入计算,编写自定义协议也更加方便;

文章有不足的地方还望指正,小子修改

当然大佬们有什么更好的方法,可以沟通交流一下

相关推荐
Cosmoshhhyyy34 分钟前
LeetCode:2270. 分割数组的方案数(遍历 Java)
java·算法·leetcode
zhulangfly36 分钟前
【Java设计模式-4】策略模式,消灭if/else迷宫的利器
java·设计模式·策略模式
夕阳之后的黑夜1 小时前
SpringBoot + 九天大模型(文生图接口)
java·spring boot·后端·ai作画
芝士就是力量啊 ೄ೨1 小时前
Kotlin 循环语句详解
android·java·开发语言·kotlin
QQ27437851092 小时前
django基于Python对西安市旅游景点的分析与研究
java·后端·python·django
会code的厨子2 小时前
Spring底层核心原理解析
java·spring
苹果酱05672 小时前
Redis之数据结构
java·spring boot·毕业设计·layui·课程设计
造梦师阿鹏2 小时前
【SpringBoot】用一个常见错误说一下@RequestParam属性
java·spring boot·后端·spring
袁庭新2 小时前
IntelliJ IDEA中Maven项目的配置、创建与导入全攻略
java·intellij-idea·袁庭新·maven工具·idea如何配置maven·maven如何使用
葡萄架子2 小时前
线程并发下的单例模式
java·开发语言·单例模式