【问题标题】:Using SO_REUSEADDR with EventMachine (over UDP)将 SO_REUSEADDR 与 EventMachine 一起使用(通过 UDP)
【发布时间】:2011-10-24 21:08:46
【问题描述】:

我正在尝试在与EventMachine 的 UDP 连接上设置套接字选项(特别是 SO_REUSEADDR)。照原样,代码 sn-p 有效。当第二个open_datagram_socket 未注释时,它将失败并出现以下错误:

eventmachine.rb:844:in `open_udp_socket': 没有数据报套接字 (RuntimeError)

从查看源代码来看,如果失败,它似乎只是返回 null,这并不奇怪。似乎套接字选项设置不正确,但我不熟悉库或 ruby​​ 或套接字编程,不知道我是否做错了什么。我无法想象图书馆不支持这样的东西,但我猜有可能。

如何让 SO_REUSEADDR 与数据报套接字一起工作?

require 'eventmachine'

class PassThruServer < EM::Connection
    def initialize
        set_sock_opt Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true
    end
    def post_init()
        # too late?
#       set_sock_opt Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true
    end
    def receive_data(data)
        puts "PT: "+ data.to_s()
        send_datagram data, "localhost", 6060
    end
end

class MessagePrinter < EM::Connection
    def receive_data(data)
        puts "MP: "+ data.to_s()
    end
end

EM.run do
    # pass through
    EM.open_datagram_socket "localhost", 5050, PassThruServer
#   EM.open_datagram_socket "localhost", 5050, PassThruServer

    # test consumer
    EM.open_datagram_socket "localhost", 6060, MessagePrinter

    # test producer
    EM.open_datagram_socket "localhost", nil do |conn|
        i = 1
        EM.add_periodic_timer(3) do
            data = "message: "+ i.to_s() +"\n"
            conn.send_datagram data, "localhost", 5050
            i += 1
        end
    end
end

看起来 TCP 可能总是使用 SO_REUSEADDR。我看不到 UDP 甚至在哪里初始化套接字选项。据我了解,它们必须在套接字实际打开之前设置?

然而,我并没有真正了解 ruby​​ 的 C 绑定来验证我是否找到了正确的位置。

【问题讨论】:

    标签: ruby eventmachine


    【解决方案1】:

    我能够获得使用以下补丁创建的套接字。但是,我无法让每个套接字都接收数据的副本。

    *** em.cpp  Tue Oct 25 10:52:22 2011
    --- em_.cpp Tue Oct 25 10:51:38 2011
    *************** EventMachine_t::OpenDatagramSocket
    *** 1572,1577 ****
    --- 1572,1578 ----
      const unsigned long EventMachine_t::OpenDatagramSocket (const char *address, int port)
      {
              unsigned long output_binding = 0;
    +         int one = 1;
    
              int sd = socket (AF_INET, SOCK_DGRAM, 0);
              if (sd == INVALID_SOCKET)
    *************** const unsigned long EventMachine_t::Open
    *** 1606,1611 ****
    --- 1607,1615 ----
                              goto fail;
              }
    
    +         if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char*) &one, sizeof(one)) < 0)
    +                 goto fail;
    + 
              if (bind (sd, (struct sockaddr*)&sin, sizeof(sin)) != 0)
                      goto fail;
    

    我认为库的作者根本没有考虑过我的用例。需要设置的另一个套接字选项是IP_ADD_MEMBERSHIP。我猜它很容易被黑进C层。不过,如果我试图将它暴露在红宝石层上,我会迷失方向。我认为这超出了我的实际能力范围。

    我怀疑使用 vanilla ruby​​ 库更合适。

    【讨论】:

      猜你喜欢
      • 2012-02-19
      • 1970-01-01
      • 1970-01-01
      • 2012-07-06
      • 1970-01-01
      • 2016-04-01
      • 2013-07-08
      • 1970-01-01
      相关资源
      最近更新 更多