Reputation: 10720
I am trying set socket options (SO_REUSEADDR specifically) on a UDP connection with EventMachine. As is, the code snippet works. When the second open_datagram_socket
is uncommented, it will fail with this error:
eventmachine.rb:844:in `open_udp_socket': no datagram socket (RuntimeError)
From looking at the source, it looks like it just returns null if it fails, which isn't surprising. It seems like the socket option just isn't being set correctly, but I am not familiar with the library or ruby or socket programming to know if I am doing something wrong. I can't imagine the library just doesn't support something like this, but it's possible I guess.
How can I get SO_REUSEADDR to work with a datagram socket?
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
It looks like TCP might always uses SO_REUSEADDR. I don't see where the UDP even initializes the socket options. As far as I understand it, they would have to be set before the socket was actually opened?
I don't really grok ruby's C bindings to verify that I am looking in the right place however.
Upvotes: 0
Views: 1032
Reputation: 10720
I was able to get the socket created with the below patch. I was not able to get the sockets to each receive a copy of the data however.
*** 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;
I don't think my use case has been considered by the authors of the library at all. The other socket option which needed to be set is IP_ADD_MEMBERSHIP
. It could be hacked into the C layer rather easily I am guessing. I would be lost trying to expose it to the ruby layer, though. I think that is beyond my practical reach.
I suspect using the vanilla ruby library is more appropriate.
Upvotes: 1