将SO_REUSEADDR与EventMachine一起使用(通过UDP)

时间:2011-10-24 21:08:47

标签: ruby eventmachine

我在与EventMachine的UDP连接上尝试设置套接字选项(特别是SO_REUSEADDR)。原样,代码片段可以正常工作。当第二个open_datagram_socket取消注释时,它将失败并显示以下错误:

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

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

如何让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绑定来验证我正在寻找合适的地方。

1 个答案:

答案 0 :(得分: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​​库更合适。