'Who's online?' Ruby Network Program - ruby

I have several embedded linux systems that I want to write a 'Who's Online?' network service in Ruby. Below is related part of my code:
mySocket = UDPSocket.new
mySocket.bind("<broadcast>", 50050)
loop do
begin
text, sender = mySocket.recvfrom(1024)
puts text
if text =~ /KNOCK KNOCK/ then
begin
sock = UDPSocket.open
sock.send(r.ipaddress, 0, sender[3], 50051)
sock.close
rescue
retry
end
end
rescue Exception => inLoopEx
puts inLoopEx.message
puts inLoopEx.backtrace.inspect
retry
end
end
I send the 'KNOCK KNOCK' command from a PC. Now, the problem is since they all receive the message at the same time, they try to respond at the same time too, which causes a Broken Pipe exception (which is the reason of my 'rescue retry' code). This code works OK sometimes but; other times the rescue retry part of the code (which is waked by Broken Pipe exception from sock.send) causes one or more systems to respond after 5 seconds or so.
Is there a better way of doing this since I assume I cant escape the Broken Pipe exception?

I have found that exception was caused by the 'r.ipaddress' part in the send command, which is related to my embedded system's internals...

Related

Skip if it doesn't respond in a certain amount of time

I've created a program that pulls websites off of google and then strips them down to their basic url: example http://google.com/search/owie/weikw => http://google.com. It then saves these to a file.
After that it runs a .each_line on the file then runs a whois command, what I want to do is if the command doesn't respond in a certain amount of time, skip that line of the file and go to the next one, is there a way I can do this?
Use the Timeout Module
If your scraper or whois doesn't support timeout natively, you can use Timeout::timeout to set an upper bound in seconds. For example:
require 'timeout'
MAX_SECONDS = 10
begin
Timeout::timeout(MAX_SECONDS) do
# run your whois
end
rescue Timeout::Error
# handle the exception
end
By default, this will raise a Timeout::Error exception if the block exceeds the time limit, but you can have the method raise other exceptions if you prefer. How you handle the exceptions is then up to you.

prevent "No buffer space available" error in ruby

I have a small script which scans all the ips ranging from 192.168.190.xxx to 192.168.220.xxx on port 411.
The script works fine sometimes, but sometimes I get the error "No buffer space available"
dcport.rb:8:ininitialize': No buffer space available - connect(2) (Errno::ENOBUFS)`
I have read that this occurs when the socket were not closed properly, but I have used mysocket.close to prevent that which I suppose does not work properly.
How to prevent this from happening, I mean how to close the socket properly?
My code is as follows
require 'socket'
require 'timeout'
(190...216).each do |i|
(0...255).each do |j|
begin
#puts "Scanning 192.168.#{i}.#{j}"
scan=Timeout::timeout(10/1000.0) {
s=TCPSocket.new("192.168.#{i}.#{j}",411)
s.close
puts "192.168.#{i}.#{j} => Hub running"
}
rescue Timeout::Error
rescue Errno::ENETUNREACH
rescue Errno::ECONNREFUSED
end
end
end
My guess is that, sometimes, the timeout fires between the socket creation and the socket closing, which makes you leak some sockets. Since (as far as a quick google search told me), ENOBUFS happens by default after 1024 sockets opened, that could definitely be it.
Timeout, as well as Thread.raise, is very harmful in situations where you need to be sure that something happens (in your case, s.close), as you actually cannot guarantee it anymore: the exception could be raised anywhere, even within an ensure block.
In your case, I think that you could fix it by adding an ensure clause outside the timeout block (untested code follows):
require 'socket'
require 'timeout'
(190...216).each do |i|
(0...255).each do |j|
begin
#puts "Scanning 192.168.#{i}.#{j}"
s = nil
scan=Timeout::timeout(10/1000.0) do
s=TCPSocket.new("192.168.#{i}.#{j}",411)
puts "192.168.#{i}.#{j} => Hub running"
end
rescue Timeout::Error
rescue Errno::ENETUNREACH
rescue Errno::ECONNREFUSED
ensure
s.close if s
end
end
end

Ruby Thread with "watchdog"

I'm implementing a ruby server for handling sockets being created from GPRS modules. The thing is that when the module powers down, there's no indication that the socket closed.
I'm doing threads to handle multiple sockets with the same server. What I'm asking is this: Is there a way to use a timer inside a thread, reset it after every socket input, and that if it hits the timeout, closes the thread? Where can I find more information about this?
EDIT: Code example that doesn't detect the socket closing
require 'socket'
server = TCPServer.open(41000)
loop do
Thread.start(server.accept) do |client|
puts "Client connected"
begin
loop do
line = client.readline
open('log.txt', 'a') { |f|
f.puts line.strip
}
end
rescue
puts "Client disconnected"
end
end
end
I think you need a heartbeat mechanism.
At a guess, your sockets are inexplably closing because you're not catching exceptions that are raised when they are closed by the remote end.
you need to wrap the connection handler in an exception catching block.
Without knowing what module/model you're using I will just fudge it and say you have a process_connection routine. So you need to do something like this:
def process_connection(conn)
begin
# do stuff
rescue Exception => e
STDERR.print "Caught exception #{e}: #{e.message}\n#{e.backtrace}\n"
ensure
conn.close
end
end
This will catch all exceptions and dump them to stderr with a stack trace. From there you can see what is causing them, and possibly handle them more gracefully elsewhere.
Just check the standar API Timeout:
require 'timeout'
status = Timeout::timeout(3){sleep(1)}
puts status.inspect
status = Timeout::timeout(1){sleep(2)}

Ruby TCPSocket doesn't notice it when server is killed

I've this ruby code that connects to a TCP server (namely, netcat). It loops 20 times, and sends "ABCD ". If I kill netcat, it takes TWO iterations of the loop for an exception to be triggered. On the first loop after netcat is killed, no exception is triggered, and "send" reports that 5 bytes have been correctly written... Which in the end is not true, since of course the server never received them.
Is there a way to work around this issue ? Right now I'm losing data : since I think it's been correctly transfered, I'm not replaying it.
#!/usr/bin/env ruby
require 'rubygems'
require 'socket'
sock = TCPSocket.new('192.168.0.10', 5443)
sock.sync = true
20.times do
sleep 2
begin
count = sock.write("ABCD ")
puts "Wrote #{count} bytes"
rescue Exception => myException
puts "Exception rescued : #{myException}"
end
end
When you're sending data your blocking call will return when the data is written to the TCP output buffer. It would only block if the buffer was full, waiting for the server to acknowledge receipt of previous data that was sent.
Once this data is in the buffer, the network drivers try to send the data. If the connection is lost, on the second attempt to write, your application discovers the broken state of the connection.
Also, how does the connection close? Is the server actively closing the connection? In which case client socket would be notified at its next socket call. Or has it crashed? Or perhaps there's a network fault which means you can no longer communicate.
Discovering a broken connection only occurs when you try to send or receive data over the socket. This is different from having the connection actively closed. You simply can't determine if the connection is still alive without doing something with it.
So try doing sock.recv(0) after the write - if the socket has failed this would raise "Errno::ECONNRESET: Connection reset by peer - recvfrom(2)". You could also try sock.sendmsg "", 0 (not sock.write, or sock.send), and this would report a "Errno::EPIPE: Broken pipe - sendmsg(2)".
Even if you got your hands on the TCP packets and get acknowledgement that the data had been received at the other end, there's still no guarantee that the server will have processed this data - it might in its input buffer but not yet processed.
All of this might help identify a broken connection earlier, but it still won't guarantee that the data was received and processed by the server. The only sure way to know that the application has processed your message is with an application level response.
I tried without the sleep function (just to make sure it wasn't putting on hold anything) and still no luck:
#!/usr/bin/env ruby
require 'rubygems'
require 'socket'
require 'activesupport' # Fixnum.seconds
sock = TCPSocket.new('127.0.0.1', 5443)
sock.sync = true
will_restart_at = Time.now + 2.seconds
should_continue = true
while should_continue
if will_restart_at <= Time.now
will_restart_at = Time.now + 2.seconds
begin
count = sock.write("ABCD ")
puts "Wrote #{count} bytes"
rescue Exception => myException
puts "Exception rescued : #{myException}"
should_continue = false
end
end
end
I analyzed with Wireshark and the two solutions are exactly behaving identically.
I think (and can't be sure) that until you actually call your_socket.write (which will not fail as the socket is still opened because you weren't probing for its possible destruction), the socket won't raise any error.
I tried to simulate this with nginx and manual TCP sockets. And look at that:
irb> sock = TCPSocket.new('127.0.0.1', 80)
=> #<TCPSocket:0xb743b824>
irb> sock.write("salut")
=> 5
irb> sock.read
=> "<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body>\r\n</body>\r\n</html>\r\n"
# Here, I kill nginx
irb> sock.write("salut")
=> 5
irb> sock.read
=> ""
irb> sock.write("salut")
Errno::EPIPE: Broken pipe
So what's the conclusion from here? Unless you're actually expecting some data from the server, you're screwed to detect that you've lost the connection :)
To detect a gracefully close, you'll have to read from the socket - read returning 0 indicates the socket has closed.
If you do need know if data got sent successfully though, there's no way other than implementing ACKs of the data at the application level.

Asynchronous alarm signal

I'm looking for a portable interface to POSIX alarm(2) (or similar) in Ruby. That's to say, I would like to be able to set a background timer to send a signal to the current process after n seconds.
I have found some good discussion from 2006 on the ruby-talk list that provides a solution using dl/import, but that's a bit of a hack (albeit a neat hack) and not very portable.
I've looked at the much-maligned Timeout module and that won't cut it under JRuby although it works fine with the traditional interpreter. My program is a small command-line shell that uses the Readline library:
TIMEOUT = 5 # seconds
loop do
input = nil
begin
Timeout.timeout(TIMEOUT) do
input = Readline::readline('> ', nil)
end
rescue Timeout::Error
puts "Timeout"
next
end
# do something with input
end
Under JRuby it seems the process blocks in the readline call and Timeout::Error is only thrown after (a) the timer expires and (b) the user enters a new line. And the exception doesn't get rescued. Hmm.
So I came up with this workaround:
require 'readline'
class TimeoutException < Exception ; end
TIMEOUT = 5 # seconds
loop do
input = nil
start_time = Time.now
thread = Thread.new { input = Readline::readline('> ', nil) }
begin
while thread.alive? do
sleep(1) # prevent CPU from melting
raise TimeoutException if(Time.now - start_time > TIMEOUT)
end
rescue TimeoutException
thread.exit
puts "Timeout"
end
# do something with input
end
This is... clunky (let's be polite). I just want alarm(2)! I don't really want to drag in non-core libraries (eg Terminator) for this. Is there a better way?
EDIT:
I can't get another alternative -- creating a thread that sleeps and then sends a signal to the process -- to work under JRuby either. Does JRuby eat signals? Example:
SIG = 'USR2'
Signal.trap(SIG) { raise }
Process.kill(SIG, Process.pid)
JRuby simply returns, Ruby returns the expected "unhandled exception" error.
I'm sorry that I don't have an answer to your larger problem of sending a signal after X seconds to a process, but it seems that all you want to do is timeout after X seconds of waiting for input, and if that's the case then I'd say you are looking for Kernel.select :D
I've personally never used this, but after doing a google for "non-blocking gets", and subsequently exploring links, I found these two to be invaluable discussions:
http://www.ruby-forum.com/topic/126795 (Discussion of multi-threaded gets)
http://www.ruby-forum.com/topic/121404 (Explanation of Kernel.select in 2nd post)
Here's a sample of how to use it. This will print out your prompt and wait for input... If there is no input after five seconds, then the program will end. If there is input, as soon as there is input it will spit it back out and end... Obviously you can modify this for your own purposes.
def prompt
STDOUT.write "> "
STDOUT.flush
end
def amusing_messages
[ "You must enter something!",
"Why did you even start me if you just wanted to stare at me?",
"Isn't there anything better you could be doing?",
"Just terminate me already... this is getting old",
"I'm waiting..."]
end
prompt
loop do
read_array, write_array, error_array = Kernel.select [STDIN], nil, nil, 5
if read_array.nil?
puts amusing_messages[rand(amusing_messages.length)]
else
puts "Result is: #{read_array[0].read_nonblock(30)}"
end
prompt
end
It's probably not as elegant as you might like, but it definitely gets the job done without mucking around with threads. Unfortunately, this won't help you should you want something more robust (timer/sending a signal to the process), and sadly, I have no clue if this works in JRuby. Would love to know if it does though :)

Resources