Sai said:
Hi all. According to you, what could be the more sexy way to write a
simple Ruby daemon (version 1.9
) which can speak and listen in the
same time on a socket (UDP in my case)?
More sexy than what - have you written something already? If so, post it
and we'll look.
Docs you need:
http://www.ruby-doc.org/stdlib/libdoc/socket/rdoc/
http://www.ruby-doc.org/docs/ProgrammingRuby/html/lib_network.html
The first of these is unfortunately incomplete, e.g. UDPSocket#send is
missing. However the second has everything you need under the UDPSocket
header.
A normal UDP server would typically have a loop where it waits for a
message, processes it, and then sends back a response to the sender.
e.g.
require 'socket'
sock = UDPSocket.new
sock.bind(Socket.gethostname, 12345)
loop do
packet, sender = sock.recvfrom(1500)
response = "hello #{packet}"
sock.send response, 0, sender[3], sender[1]
end
Test like this in another window:
$ irb
irb(main):001:0> require 'socket'
=> true
irb(main):002:0> s = UDPSocket.new
=> #<UDPSocket:0xb7d5adec>
irb(main):003:0> s.connect Socket.gethostname, 12345
=> 0
irb(main):004:0> s.send "world", 0
=> 5
irb(main):005:0> s.recvfrom(1500)
=> ["hello world", ["AF_INET", 12345, "example.com", "192.0.2.1"]]
There's nothing to stop you sending out packets asynchronously on the
same UDPSocket though, if you want to, and this can even be done in
another thread.
So if the processing of one particular packet might take a variable
amount of time, and you still want to receive and process other packets
in the mean time, you could do something like this (untested):
...
sock.bind(Socket.gethostname, 12345)
loop do
d1, d2 = sock.recvfrom
Thread.new(d1, d2) do |packet, sender|
... process 'packet'
... use sock.send to send the response to 'sender'
end
end
Note the use of arguments in Thread.new(...) to pass copies of the local
variables to the thread. If you used d1 and d2 directly, you'd find that
they changed as soon as another packet was received in the main loop,
which leads to some nasty bugs when packets arrive at a fast rate.
So it's probably safer to write it like this:
Thread.new(*sock.recvfrom) do |packet, sender|
... etc
end
Setting Thread.abort_on_exception = true is helpful when debugging this
sort of code, otherwise when an exception occurs in the thread it just
dies silently.
HTH,
Brian.