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