How to Set the Socket Timeout in Ruby

How is Ruby TCPSocket timeout defined?

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

How do I set the socket timeout in Ruby?

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

ruby 2.5: set timeout on tcp socket's read

Yes, IO::select, using recvfrom instead of gets (which might wait a while for a newline).

Set socket timeout in Ruby via SO_RCVTIMEO socket option

You can do this efficiently using select from Ruby's IO class.

IO::select takes 4 parameters. The first three are arrays of sockets to monitor and the last one is a timeout (specified in seconds).

The way select works is that it makes lists of IO objects ready for a given operation by blocking until at least one of them is ready to either be read from, written to, or wants to raise an error.

The first three arguments therefore, correspond to the different types of states to monitor.

  • Ready for reading
  • Ready for writing
  • Has pending exception

The fourth is the timeout you want to set (if any). We are going to take advantage of this parameter.

Select returns an array that contains arrays of IO objects (sockets in this case) which are deemed ready by the operating system for the particular action being monitored.

So the return value of select will look like this:

[
[sockets ready for reading],
[sockets ready for writing],
[sockets raising errors]
]

However, select returns nil if the optional timeout value is given and no IO object is ready within timeout seconds.

Therefore, if you want to do performant IO timeouts in Ruby and avoid having to use the Timeout module, you can do the following:

Let's build an example where we wait timeout seconds for a read on socket:

ready = IO.select([socket], nil, nil, timeout)

if ready
# do the read
else
# raise something that indicates a timeout
end

This has the benefit of not spinning up a new thread for each timeout (as in the Timeout module) and will make multi-threaded applications with many timeouts much faster in Ruby.

Socket timeout in Ruby

I have tested and my ISP has something to do with the time limitation issues of my longer connections.

How to correctly use receive timeout for Ruby sockets?

I'm not sure what you're trying to do, but the server never writes anything. Also, because of the way you are using tcp_server_loop, the socket never gets closed but goes out of scope, which means that it will only be closed on next garbage collection. Because you are listening for data that never comes on a socket that was never closed on the server end, that must have something to do with it. Try this for the server:

Socket.tcp_server_loop(1234) do |sock, clientinfo|
puts "Connection!"
begin
sock.puts "Welcome to Wonderland"
ensure
sock.close
end
end

As for the time, on Linux, I can only guess that it is a matter of padding. I think you are imagining the perfect sequential struct which doesn't exist. You should only really use binary structs that have been returned from C functions. And if you know the option works in C/C++, you could write a simple extension that just defines one Ruby function that sets creates a socket, sets that option, and returns it. That is probably harder than it sounds, but is a reliable option.

EDIT:

You could use Ruby's timeout library and create a timeout around the recv call. It would look like this now:

require 'socket'
require 'timeout'
sock = Socket.new( :INET, :STREAM )
time = if RUBY_PLATFORM =~ /mingw/ then 1000 else [ 1, 0 ].pack( 'L*' ) end
sock.setsockopt( Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, time )
sock.connect( Socket.sockaddr_in( 1234, "127.0.0.1" ) )
(Timeout.timeout(100) {sock.recv( 100 )}) rescue puts("Recv timed out")

Mongo Socket timeout on Azure web app deployed in a container

EDIT : The ruby driver received a commit fixing this issue. Upgrading the ruby driver version should fix the problem. Thanks D. SM for pointing it out.

If you can't / don't want to upgrade your version, the below code seems to work.

After digging everything out of the mongoid and ruby driver doc I've managed to avoid my app crashing after going idle on Read retries with these options on my mongoid.yml :

   socket_timeout: 60
heartbeat_frequency: 2
max_idle_time: 1


Related Topics



Leave a reply



Submit