[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Non blocking UDP

Leslie Viljoen

6/10/2008 10:13:00 PM

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.ht...

- but no luck yet. I tried recvfrom_nonblock as a guess but it seems to
block anyway.

Any tips?

Les

8 Answers

Joel VanderWerf

6/10/2008 10:16:00 PM

0

Leslie Viljoen wrote:
> 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.ht...
>
> - but no luck yet. I tried recvfrom_nonblock as a guess but it seems to
> block anyway.
>
> Any tips?
>
> Les

IO#select ?

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Bill Kelly

6/10/2008 10:37:00 PM

0


From: "Leslie Viljoen" <leslieviljoen@gmail.com>
>
> 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.ht...
>
> - 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://rubyeventma... ... which presumably will solve
the blocking issue.



Regards,

Bill



Leslie Viljoen

6/10/2008 10:40:00 PM

0

On Wed, Jun 11, 2008 at 12:16 AM, Joel VanderWerf
<vjoel@path.berkeley.edu> wrote:
> Leslie Viljoen wrote:
>>
>> 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.ht...
>>
>> - but no luck yet. I tried recvfrom_nonblock as a guess but it seems to
>> 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.ht... 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)

Leslie Viljoen

6/10/2008 10:44:00 PM

0

On Wed, Jun 11, 2008 at 12:37 AM, Bill Kelly <billk@cts.com> wrote:
>
> From: "Leslie Viljoen" <leslieviljoen@gmail.com>
>>
>> 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.ht...
>>
>> - 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://rubyeventma... ... 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

Joel VanderWerf

6/10/2008 10:51:00 PM

0

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?

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Joel VanderWerf

6/10/2008 10:53:00 PM

0

Bill Kelly wrote:
> I'm currently rewriting one of my UDP apps using eventmachine
> http://rubyeventma... ... which presumably will solve
> the blocking issue.

Interested to hear how that goes....

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Leslie Viljoen

6/10/2008 11:07:00 PM

0

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://rubyeventma... ... 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

Leslie Viljoen

6/10/2008 11:12:00 PM

0

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