草庐IT

java - 在 Java 中分离协议(protocol)解析器和处理程序

coder 2024-04-01 原文

我正在使用一个简单的二进制协议(protocol)。每个数据包由 10 个字节组成。第一个字节指定数据包类型。使用了很多(~50)种数据包类型。

我想为此协议(protocol)编写一个独立于数据包处理的通用解析器。所以解析器应该检测数据包类型并将数据放入适当的数据包类的实例中,该类包含协议(protocol)数据。例如,考虑以下类:当解析器检测到数据包类型 1 --> new 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。

最佳答案

这个设计问题没有完美的答案,我不想假装我的答案是完美的,但希望我对这个问题的直觉方法能教给你一些你不知道的东西!我看到您的代码中主要缺少的组件是泛型:

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 的正确方法>,因此您可以改为考虑列出类的属性文件(呃,但有其优点),或者在您的 main 方法中使用硬编码的调用序列。

因为 ParserHandler 在这里是函数式接口(interface),不要忘记你可以用 lambdas 来实现它们!您可以通过这种方式节省大量代码行。

关于java - 在 Java 中分离协议(protocol)解析器和处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24685378/

有关java - 在 Java 中分离协议(protocol)解析器和处理程序的更多相关文章

  1. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  2. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  5. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  6. ruby - 用逗号、双引号和编码解析 csv - 2

    我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

  7. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  8. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  9. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  10. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

随机推荐