对于构建自定义协议的思考(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() {//抽象出来的方法,约束子类必须实现

    }
}

消息头:

@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();
    }
}

消息体:

@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();
    }
}

消息内容:

@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();
    }
}

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

@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 转换:

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));
    }

}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值