Ruby/EventMachine数据包解析器
我正在尝试编写一个定制的EM::Protocol模块,它可以打包/解包结构化二进制数据包。数据包结构应该定义为名称/格式对,可以是字符串、其他一些易于解析的格式,也可以是某种DSL 一些快速的代码可以让大家理解这个想法:Ruby/EventMachine数据包解析器,ruby,eventmachine,Ruby,Eventmachine,我正在尝试编写一个定制的EM::Protocol模块,它可以打包/解包结构化二进制数据包。数据包结构应该定义为名称/格式对,可以是字符串、其他一些易于解析的格式,也可以是某种DSL 一些快速的代码可以让大家理解这个想法: module PacketProtocol def self.included(base) base.extend ClassMethods end def receive_data(data) # retrieve p
module PacketProtocol
def self.included(base)
base.extend ClassMethods
end
def receive_data(data)
# retrieve packet header
# find matching packet definition
# packet.unpack(data)
end
module ClassMethods
def packet(defn)
# create an instance of Packet (see blow) and shove it
# somewhere i can get to later.
end
end
end
module MyHandler
include PacketProtocol
packet '<id:S><len:S><msg:A%{len}>'
end
EM.run do
EM.start_server '0.0.0.0', 8080, MyHandler
end
模块包协议
def自带(基本)
base.extend类方法
终止
def接收_数据(数据)
#检索数据包头
#查找匹配的数据包定义
#数据包.解包(数据)
终止
模块类方法
def数据包(defn)
#创建一个数据包实例(请参见blow)并将其推入
#我以后可以去的地方。
终止
终止
终止
模块MyHandler
包含打包协议
数据包“”
终止
嗯,跑吧
EM.start_服务器“0.0.0.0”,8080,MyHandler
终止
我的目标是最小化运行时的复杂性。数据包定义在每次执行时都是静态的,因此我希望避免这种(粗糙的)实现:
class Packet
FmtSize = {
'S' => 2,
'A' => Proc.new {|fmt| fmt[1..-1].to_i }
}
def initialize(defn)
@fields = defn.scan(/<([^>]+):([^>]+)>/)
end
def pack(data)
data.values.pack @fields.map { |name, fmt| fmt % data }.join
end
def unpack(defn)
data = {}
posn = 0
@fields.each do |name, len|
fmt = len % data
len = FmtSizes[fmt[0]]
len = len.call(fmt) if len.class == Proc
data[name.to_sym] = bytes[posn..posn + len - 1].unpack(fmt)[0]
posn += len
end
data
end
end
data = { :id => 1, :len => 5, :msg = 'Hello' }
packet = Packet.new '<id:S><len:S><msg:A%{len}>'
packed = packet.pack(data)
require 'benchmark'
Benchmark.bm(7) do |x|
x.report('slow') {
100000.times do
unpacked = packet.unpack(packed)
end
}
x.report('fast') {
100000.times do
data = {}
data[:id] = packed[0..1].unpack('S' % data)
data[:len] = packed[2..3].unpack('S' % data)
data[:msg] = packed[4..8].unpack('A%{len}' % data)
end
}
end
# output:
# user system total real
# slow 1.970000 0.000000 1.970000 ( 1.965525)
# fast 0.140000 0.000000 0.140000 ( 0.146227)
类数据包
FmtSize={
'S'=>2,
'A'=>Proc.new{| fmt | fmt[1..-1]。to|i}
}
def初始化(defn)
@fields=defn.scan(/]+):([^>]+)>/)
终止
def包(数据)
data.values.pack@fields.map{| name,fmt | fmt%data}.join
终止
def拆包(defn)
数据={}
posn=0
@字段。每个do |名称,len|
fmt=长度%数据
len=FmtSizes[fmt[0]]
如果len.class==Proc,则len=len.call(fmt)
数据[name.to_sym]=字节[posn..posn+len-1]。解包(fmt)[0]
posn+=len
终止
数据
终止
终止
数据={:id=>1,:len=>5,:msg='Hello'}
packet=packet.new“”
打包=数据包。打包(数据)
需要“基准”
Benchmark.bm(7)do|x|
x、 报告(“慢”){
10万倍
拆包=包。拆包(打包)
终止
}
x、 报告(‘快速’){
10万倍
数据={}
数据[:id]=打包的[0..1]。解包('S'%data)
数据[:len]=打包的[2..3]。解包('S'%data)
数据[:msg]=packed[4..8]。解包('A%{len}'%data)
终止
}
终止
#输出:
#用户系统总真实值
#慢速1.970000 0.000000 1.970000(1.965525)
#快速0.140000 0.0000000.140000(0.146227)
在这两个示例中,使用Packet类似乎要慢几个数量级
所以。问题是:
是否有一种方法(或gem)允许您在运行时生成代码(而不是简单地求值字符串)
编辑:
刚找到。虽然它的功能集很好,但它的基准测试速度也慢得多