Hello! I need to make a UDP server that waits for clients to log on and then drives communication from the server side. This means that if a client doesn't ack subsequent comms from the server, the server needs to resend those comms a few times before giving up. I have gotten as far as a successful UDP server, but it blocks the thread forever, and having a server constantly breaking out of socket.recvfrom using Timeout does not seem to work at all (port never seems open). I need a UDP wait with a timeout. I have looked at several UDP examples, and tried the sparse docs here: http://ruby-doc.org/stdlib/libdoc/socket/rdoc/classes/Socket.html#M004528 - but no luck yet. I tried recvfrom_nonblock as a guess but it seems to block anyway. Any tips? Les
on 2008-06-11 00:14
on 2008-06-11 00:17
Leslie Viljoen wrote: > does not seem to work at all (port never seems open). I need a UDP wait > Les IO#select ?
on 2008-06-11 00:38
From: "Leslie Viljoen" <leslieviljoen@gmail.com> > with a timeout. > > I have looked at several UDP examples, and tried the sparse docs here: > http://ruby-doc.org/stdlib/libdoc/socket/rdoc/classes/Socket.html#M004528 > > - but no luck yet. I tried recvfrom_nonblock as a guess but it seems to > block anyway. Over the years, I've never been 100% successful getting nonblock semantics to work with UDP sockets on ruby. I end up with code like: require 'timeout' require 'fcntl' UDP_RECV_TIMEOUT = 0.5 # seconds # ... begin timeout(0.17) { sock = UDPSocket.open if defined? Fcntl::O_NONBLOCK if defined? Fcntl::F_GETFL sock.fcntl(Fcntl::F_SETFL, sock.fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK) else sock.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK) end end n = sock.send(cmd, 0, @server_addr, @server_port) } rescue Timeout::Error File.open("error.log", "a") {|f| f.puts "[#{Time.now}] q2cmd: timeout in UDP open/send"} end if select([sock], nil, nil, UDP_RECV_TIMEOUT) begin # note, some linux kernel versions will select() positive for a UDP # packet, but the packet has a bad checksum, and when we do recvfrom() # the packet is thrown out, and we are blocked. (I think, due to ruby's # internals, even though we're setting NONBLOCK here, doesn't help, # for some reason... i think this was explained on ruby-talk.) # Thus the 'timeout'. timeout(0.17) { resp = sock.recvfrom(65536) } rescue Timeout::Error $stdout.puts "q2cmd: Timeout::Error in sock.recvfrom !" end end . . . and it still blocks occasionally, even though the socket is in nonblocking mode. I'm currently rewriting one of my UDP apps using eventmachine http://rubyeventmachine.com/ ... which presumably will solve the blocking issue. Regards, Bill
on 2008-06-11 00:40
On Wed, Jun 11, 2008 at 12:16 AM, Joel VanderWerf <vjoel@path.berkeley.edu> wrote: >> >> block anyway. >> >> Any tips? >> >> Les > > IO#select ? Below is my guess that doesn't work - I get connection refused when trying to connect to the port. Is there any proper documentation for these esoteric methods? http://www.ruby-doc.org/core/classes/Kernel.html#M001109 doesn't help, and even the Pickaxe ed. 3 gives not much more clue than what I tried. #!/usr/bin/ruby -w require 'socket' class UdpServer def initialize(ip, port) socket = UDPSocket.new socket.bind(ip, port) loop do a = IO.select([socket], nil, nil, 5) p a sleep(1) end end end s = UdpServer.new("0.0.0.0", 7778)
on 2008-06-11 00:44
On Wed, Jun 11, 2008 at 12:37 AM, Bill Kelly <billk@cts.com> wrote: >> I have gotten as far as a successful UDP server, but it blocks the thread > > > end > # packet, but the packet has a bad checksum, and when we do > $stdout.puts "q2cmd: Timeout::Error in sock.recvfrom !" > the blocking issue. Wow. I looked briefly at Eventmachine but got the idea it was for TCP only. If it can do UDP I'll definitely use that. Thanks a lot! Les
on 2008-06-11 00:51
Leslie Viljoen wrote: > Below is my guess that doesn't work - I get connection refused when > trying to connect to the port. [~/tmp] cat serv.rb require 'socket' class UdpServer def initialize(ip, port) socket = UDPSocket.new socket.bind(ip, port) loop do a = IO.select([socket], nil, nil, 5) if a p socket.recvfrom(1000) end end end end s = UdpServer.new("0.0.0.0", 7778) [~/tmp] cat clnt.rb require 'socket' s = UDPSocket.new s.connect("0.0.0.0", 7778) s.send("foo bar", 0) [~/tmp] ruby serv.rb ["foo bar", ["AF_INET", 35088, "localhost", "127.0.0.1"]] ["foo bar", ["AF_INET", 35088, "localhost", "127.0.0.1"]] (this shows output from running clnt.rb twice) Does this still cause connection refused? Is it a firewall issue?
on 2008-06-11 00:53
Bill Kelly wrote: > I'm currently rewriting one of my UDP apps using eventmachine > http://rubyeventmachine.com/ ... which presumably will solve > the blocking issue. Interested to hear how that goes....
on 2008-06-11 01:08
On Wed, Jun 11, 2008 at 12:52 AM, Joel VanderWerf <vjoel@path.berkeley.edu> wrote: > Bill Kelly wrote: >> >> I'm currently rewriting one of my UDP apps using eventmachine >> http://rubyeventmachine.com/ ... which presumably will solve >> the blocking issue. > > Interested to hear how that goes.... My issue is that I was using netcat without -u for UDP - I somehow do these things 1am ;-) But anyway, here's the eventmachine version which I am probably going to use because it is so very nice. Thanks for your example too! require 'eventmachine' module UmmpServer def post_init puts "client connected!" end def receive_data(data) p data end end EventMachine::run do EventMachine::open_datagram_socket('10.0.0.103', 7779, UmmpServer) EventMachine::add_periodic_timer(1) { puts "." } end Brilliant hey? Les
on 2008-06-11 01:12
Here's the version where I actually send data back - note that I don't
even have
to explicitly mention the return IP and port as per usual:
require 'eventmachine'
module UmmpServer
def post_init
puts "client connected!"
end
def receive_data(data)
p data
send_data("thanks!\n") #Eventmachine will make this
return-to-sender by default
end
end
EventMachine::run do
EventMachine::open_datagram_socket('0.0.0.0', 7779, UmmpServer)
EventMachine::add_periodic_timer(1) { puts }
end
Francis, thank-you so much for this awesome software!
Les
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.