我正在使用简单的二进制协议。每个数据包由10个字节组成。第一个字节指定数据包类型。使用了很多(约50种)数据包类型。
我想为此协议编写一个通用解析器,它与数据包的处理无关。因此,解析器应该检测数据包类型并将数据放入适当的数据包类的实例中,该实例包含协议数据。例如,考虑以下类:当解析器检测到包类型1时 - >新的Type1()并读取原始字节并设置温度和湿度。类似于数据包类型2和所有其他数据包类型。
class Packet {
byte[] raw;
}
class Type1 extends Packet {
int temperature;
int humidity;
}
class Type2 extends Packet {
DateTime sunrise;
DateTime sunset;
}
由于有很多数据包类型,但每个应用程序只使用很少的数据包,因此应该可以在解析开始之前注册某些类型。忽略所有其他数据包类型。
我打算为每种数据包类型配备一个PacketParser。可能我也需要每种类型的处理程序类。 E.g:
abstract class Type1Parser {
abstract void handle(Type1 packet);
}
class Type1Parser extends PacketParser {
//how to use/set handler? how to pass packet to handler?
static public Type1Handler type1Handler = null;
@override
void parse(Packet input) {
if(type1Handler == null)
return;
Type1 packet = new Type1(input);
packet.temperature = byteToInt(input.raw, 0, 3);
packet.humidity = byteToInt(input.raw, 4, 7);
type1Handler.handle(packet);
}
}
如何连接解析器和处理程序?超越天真的方法: 程序需要实现Type1Handler并设置静态变量Type1Parser.type1Handler。
然后主解析器可能如下所示:
class MainParser {
Type1Parser type1 = new Type1Parser();
Type2Parser type2 = new Type2Parser();
...
void parse(byte[] packet) {
switch(packet[0]) {
case 1: type1.parse(packet); break;
case 2: type2.parse(packet); break;
...
}
}
}
然而,这似乎是1)很多非常相似的代码行2)很多开销,因为所有数据包解析器都被实例化,并且每个数据包都会调用parse(),即使没有注册处理程序。 / p>
如何改进此代码?
注意:解析应该对程序透明。解析代码应保留在"解析库中#34;。因此,理想情况下,该计划只会知道"类TypeXHandler和TypeX。
答案 0 :(得分:2)
这个设计问题没有完美的答案,我不想假装我的,但希望我本能地解决这个问题可以教给你一些你不知道的东西!我看到的代码中缺少的主要组件是泛型:
public interface Parser<T extends Packet> {
T parse(Packet packet);
}
public interface Handler<T extends Packet> {
void handle(T packet);
}
这样,您可以使用延迟静态初始化来管理您知道的数据包类型。我不会在这里完全充实代码,而是给你一个想法:
public class TypeRegistry {
private static Map<Integer, TypeHandlerBundle<?>> typeHandlerBundles;
static <T> register(int typeNum, Class<T> clazz, Parser<T> parser, Handler<T> handler) {
// Make bundle, add to map
}
... void parse(Packet packet) {
if (typeHandlerBundles.containsKey((int) packet[0])) {
TypeHandlerBundle<?> bundle = typeHandlerBundles.get((int) packet[0]);
bundle.parseAndHandle(packet);
}
}
}
public class TypeHandlerBundle<T extends Packet> {
...
private final Parser<T> parser;
private final Handler<T> handler;
... void parseAndHandle(Packet packet) {
T parsedPacket = parser.parse(packet);
handler.handle(parsedPacket);
}
}
...
public class Type1Processor {
static {
TypeRegistry.register(1, Type1.class, TYPE1_PARSER, TYPE1_HANDLER);
}
// Definition of constants, implementation, etc.
// ...
}
===
我省略的内容:限定符,低级实现,错误检查,同步,主方法等。根据您的设置,静态初始化可能不是调用TypeRegistry.register
的正确方法,所以你可以而是考虑一个属性文件,列出类(呃,但有其优点),或主方法中的硬编码调用序列。
由于此处Parser
和Handler
是功能接口,因此不要忘记您可以使用lambdas实现它们!您可以通过这种方式保存大量代码。
答案 1 :(得分:1)
当你说需要一个抽象类来解析数据数组时,你是对的。
package parser;
public abstract class TypeParser {
public abstract void parse(byte[] arr);
}
然后对于每种数据包类型(你说你可以有50但如果第一个字节表示数据包的类型,则可能有256种不同的类型),你可以根据需要为某种类型创建类,例如.. Type1Parser for type 1 Type122Parser用于122型。
package parser.type;
import parser.TypeParser;
public class Type1Parser extends TypeParser{
public void parse(byte[] array){
// do with the bytes of array what you want
}
}
package parser.type;
import parser.TypeParser;
public class Type122Parser extends TypeParser {
public void parse(byte[] arr) {}
}
然后你可以有一个代表所有人的主解析器的类。如果您需要每个收入数据包都有一个对象供以后使用,那么您可以将其保存在向量中。
package parser;
import java.util.Vector;
public class MainParser {
private Vector<TypeParser> vecTypeParse=new Vector<TypeParser>();
public void parsePacket(byte[] array){
if(array==null || array.length<1) return; // or throw some exception
int typePacket=array[0]&0xff;
String s="parser.type.Type"+String.valueOf(typePacket)+"Parser";
TypeParser type=null;
try {
type=(TypeParser)Class.forName(s).newInstance(); //here you create class that you need
} catch(InstantiationException e) {e.printStackTrace();
} catch(IllegalAccessException e) {e.printStackTrace();
} catch(ClassNotFoundException e) {e.printStackTrace();}
// you can do something with the exceptons
if(type==null) return; // or throw some exception
type.parse(array); // here parse data for class you just created.
this.vecTypeParse.addElement(type);
}
}
答案 2 :(得分:0)
好吧,几乎就像torquestomp的回答,这是我的代码:
interface Packet {
}
interface PacketParser<T extends Packet> {
Class<T> getPacketClass();
int getPacketId();
int getPacketLength();
Packet parse(byte[] raw, int offset);
}
interface PacketListener<T extends Packet> {
Class<T> getPacketClass();
void onPacket(T packet);
}
interface PacketParsersRegistry {
<T extends Packet> void registerPacketParser(PacketParser<T> packetParser);
<T extends Packet> void registerPacketListener(PacketListener<T> packetListener);
}
class PacketHandlers<T extends Packet> {
final PacketParser<T> parser;
PacketListener<T> listener;
PacketHandlers(PacketParser<T> parser) {
this.parser = parser;
}
void setListener(PacketListener<T> listener) {
this.listener = listener;
}
}
class MainParser implements PacketParsersRegistry {
private final HashMap<Class<?>, PacketHandlers<?>> handlers = new HashMap<>();
private final HashMap<Integer, PacketParser> parsers = new HashMap<>();
@Override
public <T extends Packet> void registerPacketParser(PacketParser<T> packetParser) {
parsers.put(packetParser.getPacketId(), packetParser);
Class<T> packetClass = packetParser.getPacketClass();
handlers.put(packetClass, new PacketHandlers<>(packetParser));
}
@Override
public <T extends Packet> void registerPacketListener(PacketListener<T> packetListener) {
//noinspection unchecked
PacketHandlers<T> handlers = (PacketHandlers<T>) this.handlers.get(packetListener.getPacketClass());
if (handlers != null) {
handlers.setListener(packetListener);
}
}
void parse(byte[] stream, int offset) {
while (offset < stream.length) {
int type = stream[offset];
PacketParser parser = parsers.get(type);
// parser m.b. != null here
PacketListener listener = (PacketListener) handlers.get(parser.getPacketClass());
if (listener != null) {
Packet packet = parser.parse(stream, offset);
//noinspection unchecked
listener.onPacket(packet);
}
offset += parser.getPacketLength();
}
}
}
以下是您可以使用它的方法:
class HumidityPacket implements Packet {}
public class Main {
public static void main(String[] args) {
MainParser parser = new MainParser();
//...
parser.registerPacketListener(new PacketListener<HumidityPacket>() {
@Override
public Class<HumidityPacket> getPacketClass() {
return HumidityPacket.class;
}
@Override
public void onPacket(HumidityPacket packet) {
// todo
}
});
}
}