通过SO_RCVTIMEO套接字选项在Ruby中设置套接字超时 [英] Set socket timeout in Ruby via SO_RCVTIMEO socket option

查看:130
本文介绍了通过SO_RCVTIMEO套接字选项在Ruby中设置套接字超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过SO_RCVTIMEO套接字选项在Ruby中使套接字超时,但它似乎对任何最近的* nix操作系统没有影响。

I'm trying to make sockets timeout in Ruby via the SO_RCVTIMEO socket option however it seems to have no effect on any recent *nix operating system.

使用Ruby的超时模块不是一个选项,因为它需要为每个超时产生和加入线程,这可能会变得昂贵。在需要低套接字超时且具有大量线程的应用程序中,它基本上会导致性能下降。许多地方都注意到了这一点,包括 Stack Overflow

Using Ruby's Timeout module is not an option as it requires spawning and joining threads for each timeout which can become expensive. In applications that require low socket timeouts and which have a high number of threads it essentially kills performance. This has been noted in many places including Stack Overflow.

我读过Mike Perham关于这个主题的优秀文章这里并努力将问题减少到一个可运行代码文件创建了一个简单的TCP服务器示例,它将接收请求,等待金额在请求中发送的时间然后关闭连接。

I've read Mike Perham's excellent post on the subject here and in an effort to reduce the problem to one file of runnable code created a simple example of a TCP server that will receive a request, wait the amount of time sent in the request and then close the connection.

客户端创建套接字,将接收超时设置为1秒,然后连接到服务器。客户端告诉服务器在5秒后关闭会话然后等待数据。

The client creates a socket, sets the receive timeout to be 1 second, and then connects to the server. The client tells the server to close the session after 5 seconds then waits for data.

客户端应该在一秒钟后超时,而是在5之后成功关闭连接。

The client should timeout after one second but instead successfully closes the connection after 5.

#!/usr/bin/env ruby
require 'socket'

def timeout
  sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)

  # Timeout set to 1 second
  timeval = [1, 0].pack("l_2")
  sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeval

  # Connect and tell the server to wait 5 seconds
  sock.connect(Socket.pack_sockaddr_in(1234, '127.0.0.1'))
  sock.write("5\n")

  # Wait for data to be sent back
  begin
    result = sock.recvfrom(1024)
    puts "session closed"
  rescue Errno::EAGAIN
    puts "timed out!"
  end
end

Thread.new do
  server = TCPServer.new(nil, 1234)
  while (session = server.accept)
    request = session.gets
    sleep request.to_i
    session.close
  end
end

timeout

我也试过用TCPSocket做同样的事情(它会自动连接)并在 redis 和其他项目。

I've tried doing the same thing with a TCPSocket as well (which connects automatically) and have seen similar code in redis and other projects.

此外,我可以通过调用 getsockopt 验证是否已设置该选项:

Additionally, I can verify that the option has been set by calling getsockopt like this:

sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO).inspect

设置此套接字选项是否真的适用于任何人?

Does setting this socket option actually work for anyone?

推荐答案

你可以这样做有效使用 s从Ruby的IO类中选出

IO :: select 需要4个参数。前三个是要监视的套接字数组,最后一个是超时(以秒为单位指定)。

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

select的工作方式是使IO对象列表准备就绪对于给定的操作,通过阻塞直到它们中的至少一个准备好被读取,写入或想要引发错误。

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.


  • 准备好阅读

  • 准备写作

  • 有待处理的异常

第四个是你要设置的超时(如果有的话) 。我们将利用这个参数。

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

选择返回一个数组,该数组包含IO对象数组(在本例中为套接字),操作系统认为这些对象已准备就绪正在监控的特定行动。

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.

所以select的返回值如下所示:

So the return value of select will look like this:

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

但是,如果给出了可选的超时值并且在超时秒内没有准备好IO对象,则选择返回 nil

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

因此,如果您想在Ruby中执行高性能IO超时并避免使用Timeout模块,则可以执行以下操作:

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

这样做的好处是不会为每个超时启动一个新线程(如在Timeout模块中),并且将生成多线程应用程序Ruby中有很多超时更快。

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.

这篇关于通过SO_RCVTIMEO套接字选项在Ruby中设置套接字超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆