From: Leslie Viljoen Date: 2008-06-11T07:43:44+09:00 Subject: Re: Non blocking UDP On Wed, Jun 11, 2008 at 12:37 AM, Bill Kelly wrote: > > From: "Leslie Viljoen" >> >> 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. > > 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. 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