你好男孩和女孩。
我正在开发一个基于终端的客户端应用程序,它通过TCP/IP与服务器通信,并发送和接收任意数量的原始字节。每个字节代表一个命令,我需要将其解析为表示这些命令的 Java 类,以供进一步使用。
我的问题应该如何有效地解析这些字节。我不想以一堆嵌套的 if 和开关案例结束。
我已经准备好了这些命令的数据类。我只需要找出正确的解析方法。
下面是一些示例规格:
字节流可以是例如 整数:[1,24,2,65,26,18,3,0,239,19,0,14,0,42,65,110,110,97,32,109,121,121,106,228,42,15,20,5,149,45,87]
第一个字节是0x01,它是仅包含一个字节的标头的开头。
第二个是长度,即某些字节数 命令,这里也只有一个字节。
下一个可以是第一个字节是命令的任何命令,0x02 在这种情况下,它遵循 n 个字节,这些字节包含在 命令。
等等。最后有与校验和相关的字节。
表示 set_cursor 命令的示例类:
/**
* Sets the cursor position.
* Syntax: 0x0E | position
*/
public class SET_CURSOR {
private final int hexCommand = 0x0e;
private int position;
public SET_CURSOR(int position) {
}
public int getPosition() {
return position;
}
public int getHexCommnad() {
return hexCommand;
}
}
像这样解析字节流时,最好的设计模式是命令模式。 每个不同的命令将充当处理程序来处理流中接下来的几个字节。
interface Command{
//depending on your situation,
//either use InputStream if you don't know
//how many bytes each Command will use
// or the the commands will use an unknown number of bytes
//or a large number of bytes that performance
//would be affected by copying everything.
void execute(InputStream in);
//or you can use an array if the
//if the number of bytes is known and small.
void execute( byte[] data);
}
然后,您可以拥有一个映射,其中包含每个字节"操作码"的每个命令对象。
Map<Byte, Command> commands = ...
commands.put(Byte.parseByte("0x0e", 16), new SetCursorCommand() );
...
然后,您可以解析消息并执行命令:
InputStream in = ... //our byte array as inputstream
byte header = (byte)in.read();
int length = in.read();
byte commandKey = (byte)in.read();
byte[] data = new byte[length]
in.read(data);
Command command = commands.get(commandKey);
command.execute(data);
您可以在同一字节消息中有多个命令吗? 如果是这样,您可以轻松地将命令获取和解析包装在一个循环中,直到 EOF。
你可以试试JBBP库来 https://github.com/raydac/java-binary-block-parser
@Bin class Parsed { byte header; byte command; byte [] data; int checksum;}
Parsed parsed = JBBPParser.prepare("byte header; ubyte len; byte command; byte [len] data; int checksum;").parse(theArray).mapTo(Parsed.class);
这是一个庞大而复杂的主题。
这取决于您将读取的数据类型。
- 是一条嘻嘻哈哈的溪流吗?
- 是很多小的独立结构/对象吗?
- 您的流的结构/对象之间是否有一些引用?
我最近为一个专有软件编写了一个字节序列化/反序列化库。
我采用了类似访问者的类型转换方法,与 JAXB 的工作方式相同。
我将我的对象定义为 Java 类。 初始化类上的解析器,然后向其传递要取消序列化的字节或要序列化的 Java 对象。
类型检测(基于流的第一个字节)通过简单的大小写匹配机制(1 => 类 A、15 => 类 F 等)向前完成。
编辑:它可能很复杂或代码过载(嵌入对象),但请记住,如今,java很好地优化了这一点,它使代码保持清晰易懂。
ByteBuffer
可用于解析字节流 - ByteBuffer 在 Java 中的用途是什么?
byte[] bytesArray = {4, 2, 6, 5, 3, 2, 1};
ByteBuffer bb = ByteBuffer.wrap(bytesArray);
int intFromBB = bb.order(ByteOrder.LITTLE_ENDIAN).getInt();
byte byteFromBB = bb.get();
short shortFromBB = bb.getShort();