我为一个小型多人游戏实现了一个协议。它基于字节,因此为了反序列化接收到的消息,我必须遍历字节流并逐位解析它。在获得所有字节并知道消息类型后,我将字节放入反向构造函数中,该构造函数从原始字节构造协议数据单元。
这整个过程非常丑陋,不是真正的 OO 并且有不可读的if/else代码。我必须为reverseConstructor(byte[] bytes)
我添加的每个协议数据单元 (pdu) 实现。每个 pdu 定义某种模式的方法(例如 schema = [1 byte int (id = x), x bytes ascii string, 4 bytes double]),并且使用该模式完成字节的处理,将更优雅。
我在这里得到了关于使用 google protobufs 的提示 (显然它们不符合我的需求,因为我必须更改协议以遵守 protobuf 标准)。
信息
我无法更改协议。有两种不同的场景(我不想同时支持它们甚至在同一个程序中):
- 协议数据单元在标头中编码了一个长度字段
- 协议数据单元没有长度字段,但可以从消息类型派生出消息何时/何地结束。
我个人是长度字段的粉丝。但有时您必须遵守其他人设计的协议。所以协议是固定的。它们都有一个标头,其中包含协议 id、唯一消息 id以及在第一种情况下的长度字段。
问题
谁能给我一个非常小的例子,两个简单的协议数据单元由高效的通用接收方法解析?我在 protobuf 教程中找到的唯一示例是以下类型:用户 a 发送消息 x,用户 b 期望消息 X,并且可以毫无问题地反序列化它。
但是如果用户 b 必须为消息 x、y 和 z 做好准备呢?如何以一种智能的方式在没有大量代码重复的情况下处理这种情况。
我也很欣赏设计原则的提示,使我能够在不使用外部库的情况下在这里实现更好的代码。
编辑
我认为这样是要走的路。您可以在此处找到更多代码。字节被动态读取,直到找到一个对象,然后缓冲区的位置被重置。
while (true) {
if (buffer.remaining() < frameLength) {
buffer.reset();
break;
}
if (frameLength > 0) {
Object resultObj = prototype.newBuilderForType().mergeFrom(buffer.array(), buffer.arrayOffset() + buffer.position(), frameLength).build();
client.fireMessageReceived(resultObj);
buffer.position(buffer.position() + frameLength);
buffer.mark();
}
if (buffer.remaining() > fieldSize) {
frameLength = getFrameLength(buffer);
} else {
break;
}
}
JavaDoc- mergeFrom
将数据解析为这种类型的消息,并将其与正在构建的消息合并。这只是 MessageLite.Builder.mergeFrom(CodedInputStream) 的一个小包装。 https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/Message.Builder#mergeFrom(byte[])
问题是这种类型的部分消息,但应该可以用通用方法解决这个问题。
样本
这是一个示例协议数据单元。它有一个长度字段。还有另一种情况,PDU 没有长度字段。这个 pdu 的大小是可变的。还有固定大小的PDU。
为了完整起见。这里是协议数据单元中字符串的表示。