Gem Ruby 如何与 C 中的 struct 结构体进行 TCP 的交互?

mingle5566 · 2015年10月26日 · 最后由 mingle5566 回复于 2015年10月27日 · 2714 次阅读

最近在使用 ruby 进行与路由器交互的开发,路由硬件使用的 C 有没有 gem 能够很好的方便与 C 中的 struct 结构体进行交互,包括 send、recv ???

  1. Ruby 标准库: Fiddle
  2. gem FFI

这两者都有对 C struct 的支持。 Fiddle::CStruct 和 FFI::Struct. 具体使用参考其文档即可。

1.thrift 2.Hprose

cstruct #1 楼 @skandhas 这两个只是调用 c 库,生成 struct 结构。现在我是想不改变 C 路由端的代码,直接用 ruby 接收 C 发过来的结构字节流?

#3 楼 @mingle5566 这个 socket 不就行了

#3 楼 @mingle5566 这个当然也可以。用这个 gem bit-struct 或 gem cstruct(不是 Fiddle::CStruct)

仅为 示范性伪代码

require 'bit-struct'
class MyStruct < BitStruct
    ...
    unsigned    :id,   8,     "id"     # 8 意为 8bit,也就一个byte。一个int32类型,就是32。以此类推。
   ...
end

 data = recv(...)
 my_data = MyStruct.new(data)
puts my_data.id

#4 楼 @flowerwrong 嗯,会用到 socket,问题在于要处理 bit 中的结构体

#5 楼 @skandhas 谢谢~这个 gem 正好能达到我的需求,前两天在 Google 查了半天也没找到合适的~ 真是万分感谢🙏

#2 楼 @xxqfamous 谢谢,第三方的中间件也可以考虑考虑

#6 楼 @mingle5566 这个和结构体没关系吧,这是面向协议编程. 解析协议我基本都用 erlang. #5 楼 @skandhas 那个 bit-struct 挺方便的啊. 或者自己进行位操作. 例如 ruby 解析 websocket process_data

#7 楼 @mingle5566 不客气,能用上就 OK 了。 #9 楼 @flowerwrong 嗯,如果结构很简单,自己进行 位操作 和 unpack 也是不错的选择。

#10 楼 @skandhas 还有个问题请教一下,用 bit-struct 如何解析 char[128] 这种固定位数的值,例如:

###################C中我这样定义的

typedef unsigned char  __u8;
typedef unsigned short __u16;
typedef unsigned int   __u32;

typedef struct
{
  __u16 order;              /*表示连接认证时的交互顺序,由1开始逐1地递增。路由器与服务器间共享同一个order,比如:路由器order = 1, 服务器order = 2, 路由器order = 3, 目前最多到4*/
    __u8 srv_type;
    __u8 router_addr[128];    /*设备地址*/
}sentrtu;

sentrtu seru;

seru.order = htons(1025);
seru.srv_type = 'b';
strcpy(seru.router_addr, "abc");

####################ruby这样定义
class MyStruct < BitStruct
     unsigned :order, 16, "order"
     unsigned :srv_type, 8, "srv_type"
     unsigned :router_addr, 16 * 8, "router_addr"
end

my_data = MyStruct.new(data)
puts my_data.order
puts my_data.srv_type.chr
puts my_data.router_addr

最后 router_addr 输入的是: 129445968641824014294637244265334308864 一串数字,这个是什么格式的?如何转换输入"abc"

#11 楼 @mingle5566 对于数组的处理用 vector, 不过 bit-struct 对 array 的 处理比较啰嗦。

require "bit-struct"

class SentRTU < BitStruct
  unsigned :order,    16, 'order'
  unsigned :srv_type, 8,  'srv-type'

  vector :router_addr,   "router_addr", :length => 128 do
    unsigned :byte, 8 
  end 

  def set_router_addr(str)
    if str.length > router_addr.size - 1
       raise "str.length should be less than router_addr.size - 1"
    end

    # bit-struct 对 vector 的写入处理比较啰嗦,只能挨个赋值。
    # 像这样是不行的:
    #   str.bytes.each_with_index {|byte, i| router_addr[i].byte = byte }

    addr = self.router_addr
    str.bytes.each_with_index do |byte, i| 
      v = addr[i]
      v.byte = byte
      addr[i] = v
    end 
    self.router_addr = addr
  end
end


a = SentRTU.new
a.set_router_addr('abcdef')
puts a.router_addr

类似的 gem 还有 binstruct, cstruct . 在使用类似的 gem 时,一定要注意 struct 的 alignment 与相应的 C struct 的 alignment 要一致,不然就不对了。

#12 楼 @skandhas 受益颇多~继续学习中~👍👍

14楼 已删除
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册