2014-07-10 47 views
6

我正在使用简单的二进制协议。每个数据包由10个字节组成。第一个字节指定数据包类型。有许多(〜50)数据包类型使用。在Java中分离协议解析器和处理程序

我想为这个协议编写一个独立于数据包处理的通用解析器。所以解析器应该检测数据包类型并将数据放入适当数据包类的实例中,该类包含协议数据。例如,考虑以下类别:解析器检测到数据包类型1 - >新的Type1()并读取原始字节并设置温度和湿度。对于数据包类型2和所有其他数据包类型也是如此。

class Packet { 
    byte[] raw; 
} 

class Type1 extends Packet { 
    int temperature; 
    int humidity; 
} 

class Type2 extends Packet { 
    DateTime sunrise; 
    DateTime sunset; 
} 

既然有这么多的数据包类型,但每个应用程序只使用很少的,它应该是可能的分析开始之前对某些类型的注册。所有其他数据包类型都被忽略。

我打算为每个数据包类型分配一个PacketParser。也许,我需要每种类型的处理程序类。例如:

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(),即使没有注册处理程序。

任何想法如何改善此代码?

注意:解析应该对程序透明。解析代码应该留在“解析库”中。所以理想情况下,程序只“知道”类TypeXHandler和TypeX。

+0

“对于每个数据包parse()被调用,即使没有注册处理程序”。 - 似乎有必要调用解析器,至少在输入流中跳过数据包的字节。您可以读取数据包类型并跳过剩余的解析,只需跳过数据包长度(我假设每个数据包类型都具有固定长度)。 –

+0

处理程序是您想要将数据包信息传递给哪个代码的部分?它应该做什么? – NESPowerGlove

+0

为了摆脱* parse中的* some *重复,你可以不用'PacketParser解析器;'使用开关来确定和设置解析器,然后在开关外侧执行'parser.parse(packet);'?我知道这并不深刻,但确实减少了逐字。 – ChiefTwoPencils

回答

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 
      } 
     }); 
    } 
} 
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的正确方法,所以你可以考虑一个属性文件来列出这些类(呃,但有其优点),或者是一个硬编码的序列调用你的主要方法。

由于ParserHandler在这里是功能接口,不要忘了你可以用lambdas实现它们!您可以通过这种方式节省大量的代码行。

1

当你说你是正确的,需要一个抽象类解析数据数组。

package parser; 

    public abstract class TypeParser { 
     public abstract void parse(byte[] arr); 

    } 

然后为每个数据包的类型(你说,你可以有50元,但如果第一个字节表示数据包的类型,然后256种输精管类型是可能的),您可以根据您的需要为某些类型如创建类。 。Type1Parser 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);  
     } 

}