How do you set the timeout for blocking operations on a Ruby socket?
The solution I found which appears to work is to use Timeout::timeout:
require 'timeout'
...
begin
timeout(5) do
message, client_address = some_socket.recvfrom(1024)
end
rescue Timeout::Error
puts "Timed out!"
end
The timeout object is a good solution.
This is a example of asynchronous I/O (nonblocking in nature and occurs asynchronously to the flow of the application.)
IO.select(read_array [, write_array [, error_array [, timeout]]] ) => array or nil
Can be used to get the same effect.
require 'socket'
strmSock1 = TCPSocket::new( "www.dn.se", 80 )
strmSock2 = TCPSocket::new( "www.svd.se", 80 )
# Block until one or more events are received
#result = select( [strmSock1, strmSock2, STDIN], nil, nil )
timeout=5
timeout=100
result = select( [strmSock1, strmSock2], nil, nil,timeout )
puts result.inspect
if result
for inp in result[0]
if inp == strmSock1 then
# data avail on strmSock1
puts "data avail on strmSock1"
elsif inp == strmSock2 then
# data avail on strmSock2
puts "data avail on strmSock2"
elsif inp == STDIN
# data avail on STDIN
puts "data avail on STDIN"
end
end
end
This article details some problems that the Timeout module can experience. This article shows a way of timing out sockets that might be more performant.
I think the non blocking approach is the way to go.
I tried the mentioned above article and could still get it to hang.
this article non blocking networking and the jonke's approach above got me on the right path. My server was blocking on the initial connect so I needed it to be a little lower level.
the socket rdoc can give more details into the connect_nonblock
def self.open(host, port, timeout=10)
addr = Socket.getaddrinfo(host, nil)
sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
begin
sock.connect_nonblock(Socket.pack_sockaddr_in(port, addr[0][3]))
rescue Errno::EINPROGRESS
resp = IO.select([sock],nil, nil, timeout.to_i)
if resp.nil?
raise Errno::ECONNREFUSED
end
begin
sock.connect_nonblock(Socket.pack_sockaddr_in(port, addr[0][3]))
rescue Errno::EISCONN
end
end
sock
end
to get a good test. startup a simple socket server and then do a ctrl-z to background it
the IO.select is expecting data to come in on the input stream within 10 seconds. this may not work if that is not the case.
It should be a good replacement for the TCPSocket's open method.