2014-07-10 49 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);  
     } 

}