我正在为许多不同的协议编写network protocol decoder。此应用程序是一个命令行应用程序,不使用Rails。
示例代码以说明我的挑战(Live Demo):
#!/usr/bin/env ruby
# define the base protocol handler
module Base
MSG_HASH = {0x30=>:One,0x31=>:Two,0x32=>:Three}
class Decoder
def decode(wire)
# get the msg type
msg_type = MSG_HASH[wire]
# delegate to a handler for that msg type
send("decode_#{msg_type}_message".to_sym)
end
def decode_One_message
"This was a One message"
end
def decode_Two_message
"This was a Two message"
end
end
end
# define a handler for a specialized protocol
module Special
MSG_HASH = Base::MSG_HASH.merge({0x4a=>:Foo, 0x73=>:Bar})
class Decoder < Base::Decoder
def decode_Foo_msg
"Specialized Foo message"
end
def decode_Bar_message
"Specialized Bar message"
end
end
end
# define a handler for another specialized protocol
module AnotherSpecial
MSG_HASH = Base::MSG_HASH.merge({0x4a=>:Zippity, 0x73=>:DooDaa})
class Decoder < Base::Decoder
def decode_Zippity_message
"Zippity"
end
def decode_DooDaa_message
"DooDaa"
end
end
end
# decode the first specialized protocol
puts "Decoding the first protocol"
[0x30, 0x31, 0x4A, 0x73].each do |wire|
decoder = Special::Decoder.new
puts "(#{wire}) decoded to '#{decoder.decode(wire)}'"
end
# decode the second specialized protocol
puts "Decoding the second protocol"
[0x30, 0x31, 0x4A, 0x73].each do |wire|
decoder = AnotherSpecial::Decoder.new
puts "(#{wire}) decoded to '#{decoder.decode(wire)}'"
end
incorect输出是:
Decoding the first protocol
(48) decoded to 'This was a One message'
(49) decoded to 'This was a Two message'
./hacks:13:in `decode': undefined method `decode__message' for #<Special::Decoder:0x00000001ccac58> (NoMethodError)
from ./hacks:59:in `block in <main>'
from ./hacks:57:in `each'
from ./hacks:57:in `<main>'
但我希望它是:
Decoding the first protocol
(48) decoded to 'This was a One message'
(49) decoded to 'This was a Two message'
(74) decoded to 'Specialized Foo message'
(115) decoded to 'Specialized Bar message'
Decoding the second protocol
(48) decoded to 'This was a One message'
(49) decoded to 'This was a Two message'
(74) decoded to 'Zippity'
(115) decoded to 'DooDaa'
我认为这里的基本问题是Base::Decoder::decode
不知道添加到Special::MSG_HASH
的额外值。怎么可能,对吧?
我的基本约束是Base
模块无法知道关于专用协议的任何,或者处理这些专用协议的模块或类中的任何内容。例如,将Base::decoder
更改为提及Special
或AnotherSpecial
模块或其中的任何内容都不适用于我。
我该如何解决这个问题?
不要害怕将我的代码撕成碎片。我完全不相信我的解决方案不是完全紊乱。
如果您对我的约束感到疑惑,请解释一下。我正在为网络协议编写解码器。这些协议中的一些共享公共子协议,并且在这些子协议内是字段(如消息类型),其可以具有由子协议或专用协议定义的值。
一个例子是消息类型。消息类型字段出现在共享XMT子协议的所有消息上。消息类型标识的一些消息在XMT协议中定义,其他消息由专用协议定义。这些专用消息类型对于专用协议是唯一的,并且可能存在重叠。例如,在ABC专用协议中,消息类型0x77可能表示Quote
,而在XYZ协议中,它可能表示Replay
。因此专用消息类型只能由专用协议的处理程序定义。但是消息类型0x30由XMT协议定义,并且始终表示Heartbeat
。
答案 0 :(得分:2)
使其工作的一种方法是将专用消息哈希移动到特殊类中,然后使用self.class::MSG_HASH
访问它。
这是完整的代码;它给出了你想要的输出:
module Base
MSG_HASH = {0x30=>:One, 0x31=>:Two, 0x32=>:Three}
class Decoder
def decode(wire)
msg_type = self.class::MSG_HASH[wire]
send("decode_#{msg_type}_message".to_sym)
end
def decode_One_message
"This was a One message"
end
def decode_Two_message
"This was a Two message"
end
end
end
module Special
class Decoder < Base::Decoder
MSG_HASH = Base::MSG_HASH.merge({0x4a => :Foo, 0x73 => :Bar})
def decode_Foo_message
"Specialized Foo message"
end
def decode_Bar_message
"Specialized Bar message"
end
end
end
module AnotherSpecial
class Decoder < Base::Decoder
MSG_HASH = Base::MSG_HASH.merge({0x4a => :Zippity, 0x73 => :DooDaa})
def decode_Zippity_message
"Zippity"
end
def decode_DooDaa_message
"DooDaa"
end
end
end
puts "Decoding the first protocol"
[0x30, 0x31, 0x4A, 0x73].each do |wire|
decoder = Special::Decoder.new
puts "(#{wire}) decoded to '#{decoder.decode(wire)}'"
end
puts "Decoding the second protocol"
[0x30, 0x31, 0x4A, 0x73].each do |wire|
decoder = AnotherSpecial::Decoder.new
puts "(#{wire}) decoded to '#{decoder.decode(wire)}'"
end
免责声明:我只在JRuby中测试了这个,而不是MRI。
答案 1 :(得分:2)
编辑:约翰,有一件事困扰我整个方法就是需要调用
decoder = XX::Decoder.new
其中XX
是Special
,AnotherSpecial
等。根据我对问题的理解,在我看来,能够添加,删除或重命名协议会更好模块没有对其他代码进行任何更改,并且在解码协议时使用嵌套类。
我可以想到几种方法可以做到这一点。两者的核心思想是每个协议处理程序都有两个方法可以由Base::Decorder#decode
调用,即:can_decode?(identifier)
将返回一个truthy,指示它是否可以解码由{{1}标识的协议};和identifier
的目的很明显。
方法1
创建一个模块decode(message, other_params)
,其中包含(可能是Protocols
)所有协议处理程序模块/类。这些类不一定是include
的子类。然后使用Module#included_modules和其他方法(例如,请参阅this和this)来创建Base::Decoder
中所有协议类的数组:
Base::Decoder
你要初始化
@protocol_classes
并通过包含
进行访问@protocol_classes = []
要使用class << self
attr_reader :protocol_classes
end
标识的协议解码邮件,identifier
会调用:
Base::Decoder:decode
方法2
当我看到你的问题时,我立即想到了Russ Olsen在他出色的着作 Eloquent Ruby (第20章)中描述的一种技术,它依赖于钩子Class#inherited。方法1的唯一区别是协议类将是decoder_class = @protocol_classes.find { |c| c.can_decode?(identifier) }
if decoder_class
decoder_class.decode(message, other_params)
else
...
的子类,就像现在一样,您可以通过将以下内容添加到Base::Decoder
来创建数组@protocol_classes:
Base::Decorder#decode
同样,这两种方法的优点在于可以添加,删除和重命名协议模块,而不需要对其余代码进行任何更改。
编辑结束。
以下是否适合您?你会看到我只是让def self.inherited(subclass)
Decoder.protocol_classes << subclass
end
要求调用类将哈希与Base::Decoder#decode
合并。
MSG_HASH
定义专用协议的处理程序
module Base
MSG_HASH = {0x30=>:One,0x31=>:Two,0x32=>:Three}
class Decoder
def decode(wire)
h = MSG_HASH.merge(self.class.hash_ext)
# get the msg type
msg_type = h[wire]
# delegate to a handler for that msg type
send("decode_#{msg_type}_message".to_sym)
end
def decode_One_message
"This was a One message"
end
def decode_Two_message
"This was a Two message"
end
end
end
为另一个专门协议定义处理程序
module Special
MSG_HASH = {0x4a=>:Foo, 0x73=>:Bar}
class Decoder < Base::Decoder
def self.hash_ext
MSG_HASH
end
def decode_Foo_message
"Specialized Foo message"
end
def decode_Bar_message
"Specialized Bar message"
end
end
end
解码第一个专用协议
module AnotherSpecial
MSG_HASH = {0x4a=>:Zippity, 0x73=>:DooDaa}
class Decoder < Base::Decoder
def self.hash_ext
MSG_HASH
end
def decode_Zippity_message
"Zippity"
end
def decode_DooDaa_message
"DooDaa"
end
end
end
解码第二个专用协议
puts "Decoding the first protocol"
[0x30, 0x31, 0x4A, 0x73].each do |wire|
decoder = Special::Decoder.new
puts "(#{wire}) decoded to '#{decoder.decode(wire)}'"
end
(48) decoded to 'This was a One message'
(49) decoded to 'This was a Two message'
(74) decoded to 'Specialized Foo message'
(115) decoded to 'Specialized Bar message'
=> [48, 49, 74, 115]
或者,为了提供更大的灵活性,可以替换
puts "Decoding the second protocol"
[0x30, 0x31, 0x4A, 0x73].each do |wire|
decoder = AnotherSpecial::Decoder.new
puts "(#{wire}) decoded to '#{decoder.decode(wire)}'"
end
(48) decoded to 'This was a One message'
(49) decoded to 'This was a Two message'
(74) decoded to 'Zippity'
(115) decoded to 'DooDaa'
=> [48, 49, 74, 115]
<{1>}中的
h = MSG_HASH.merge(self.class.hash_ext)
并使用
替换Base::Decoder#decode
和h = self.class.hash_ext(MSG_HASH)
模块中的类方法hash_ext
Special
或其他内容,具体取决于要求。
答案 2 :(得分:1)
您可以删除Special::MSG_HASH
常量并将以下行添加到Special::Decoder
类:
Base::MSG_HASH.merge!(0x4a => :Foo, 0x73 => :Bar)
但请注意,这将修改Base::MSG_HASH
常量。
您还定义了decode_Foo_msg
,您可能想要输入decode_Foo_message
。
无论如何,你应该看一下Forwardable
模块。