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.
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
timeout for blocking TCP socket not working
SO_RCVTIMEO
is no supported in a blocking socket.
If a blocking receive call times out, the connection is in an
indeterminate state and should be closed. If the socket is created
using the WSASocket function, then the dwFlags parameter must have the
WSA_FLAG_OVERLAPPED attribute set for the timeout to function
properly. Otherwise the timeout never takes effect.
Using the WSASocket
with WSA_FLAG_OVERLAPPED
. Or socket()
(default for WSA_FLAG_OVERLAPPED
mode)
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).
Socket C - setsockopt timeout do some things before closing
I have found a method :
The socket do the recv() before closing, so I check if the socket recive "" and I can send my message. The socket closing be himself after that.
What's the correct way to check if a host-alive and handle timeouts efficiently?
We make extensive use of Net::SSH in one of our systems, and ran into timeout issues.
Probably the biggest fix was to implement use of the select
method, to set a low-level timeout, and not try to use the Timeout class, which is thread based.
"How do I set the socket timeout in Ruby?" and "Set socket timeout in Ruby via SO_RCVTIMEO socket option" have code to investigate for that. Also, one of those links to "Socket Timeouts in Ruby" which has useful code, however be aware that it was written for Ruby 1.8.6.
The version of Ruby can make a difference too. Pre-1.9 the threading wasn't capable of stopping a blocking IP session so the code would hang until the socket timed out, then the Timeout would fire. Both the above questions go over that.
Related Topics
Unresolved Specs During Gem::Specification.Reset:
Find Unused Code in a Rails App
Ruby Method Lookup Path For an Object
How to Read Lines of a File in Ruby
Undefined Method 'Visit' When Using Rspec and Capybara in Rails
Ruby Local Variable Is Undefined
Validation Failed: Upload File Has an Extension That Does Not Match Its Contents
Turning Long Fixed Number to Array Ruby
Rails Respond_To Format.Js API
Pass a Variable into a Partial, Rails 3
How to Read a User Uploaded File, Without Saving It to the Database
Why Use "Self" to Access Activerecord/Rails Model Properties
How to Install Therubyracer Gem on 10.10 Yosemite
Devise Custom Routes and Login Pages
Running Another Ruby Script from a Ruby Script
Rails Installation Failed on Ubuntu With "Cannot Load Such File - Mkmf"