$ irb
1.9.3-p448 :001 > require 'socket'
=> true
1.9.3-p448 :002 > TCPSocket.new('www.example.com', 111)
gives
Errno::ETIMEDOUT: Operation timed out - connect(2)
Questions:
TCPSocket.new
?At least since 2.0 one can simply use Socket::tcp
:
Socket.tcp("www.ruby-lang.org", 10567, connect_timeout: 5) {}
Note the block at the end of the expression, which is used to get connection closed in case such is established.
For older versions @falstru answer appears to be best.
Use begin .. rescue Errno::ETIMEDOUT
to catch the timeout:
require 'socket'
begin
TCPSocket.new('www.example.com', 111)
rescue Errno::ETIMEDOUT
p 'timeout'
end
To catch any socket exceptions, use SystemCallError
instead.
According to the SystemCallError documentation:
SystemCallError is the base class for all low-level platform-dependent errors.
The errors available on the current platform are subclasses of SystemCallError and are defined in the Errno module.
TCPSocket.new
does not support timeout directly.
Use Socket::connect_non_blocking
and IO::select
to set timeout.
require 'socket'
def connect(host, port, timeout = 5)
# Convert the passed host into structures the non-blocking calls
# can deal with
addr = Socket.getaddrinfo(host, nil)
sockaddr = Socket.pack_sockaddr_in(port, addr[0][4])
Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0).tap do |socket|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
begin
# Initiate the socket connection in the background. If it doesn't fail
# immediatelyit will raise an IO::WaitWritable (Errno::EINPROGRESS)
# indicating the connection is in progress.
socket.connect_nonblock(sockaddr)
rescue IO::WaitWritable
# IO.select will block until the socket is writable or the timeout
# is exceeded - whichever comes first.
if IO.select(nil, [socket], nil, timeout)
begin
# Verify there is now a good connection
socket.connect_nonblock(sockaddr)
rescue Errno::EISCONN
# Good news everybody, the socket is connected!
rescue
# An unexpected exception was raised - the connection is no good.
socket.close
raise
end
else
# IO.select returns nil when the socket is not ready before timeout
# seconds have elapsed
socket.close
raise "Connection timeout"
end
end
end
end
connect('www.example.com', 111, 2)
The above code comes from "Setting a Socket Connection Timeout in Ruby".
If you like the idea of avoiding the pitfalls of Timeout, but prefer to avoid having to deal with your own implementation of the *_nonblock
+select
implementation, you can use the tcp_timeout
gem.
The tcp_timeout gem monkey-patches TCPSocket#connect, #read, and #write so that they use non-blocking I/O and have timeouts that you can enable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With