对于构建自定义协议的思考(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));
    }

}

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

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

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

相关推荐
weixin_441455267 分钟前
说说Java有哪些集合类
java·开发语言
合作小小程序员小小店14 分钟前
web网页开发,在线%台球俱乐部管理%系统,基于Idea,html,css,jQuery,jsp,java,ssm,mysql。
java·前端·jdk·intellij-idea·jquery·web
njnu@liyong32 分钟前
HTTP-http是什么?
网络·网络协议·http
程序定小飞1 小时前
基于springboot的作业管理系统设计与实现
java·开发语言·spring boot·后端·spring
晓庆的故事簿1 小时前
windows下载和使用minio,结合java和vue上传文件
java·开发语言
程序员小假1 小时前
我们来说一下 Mybatis 的缓存机制
java·后端
qq_336313931 小时前
java基础-学生管理系统升级
java
猫头虎1 小时前
永久免费白嫖多个域名,一键托管Cloudflare,免费申请SSL加密证书,轻松建站、搭建线路伪装
服务器·开发语言·网络·数据库·python·网络协议·ssl
弥巷1 小时前
【Android】Android内存缓存LruCache与DiskLruCache的使用及实现原理
android·java
好好沉淀2 小时前
Apache 工具包(commons-io commons-lang3 )保姆介绍
java·ide