【问题标题】:How is Ruby TCPSocket timeout defined?Ruby TCPSocket 超时是如何定义的?
【发布时间】:2014-01-27 15:39:55
【问题描述】:
$ irb
1.9.3-p448 :001 > require 'socket'
 => true 
1.9.3-p448 :002 > TCPSocket.new('www.example.com', 111)

给予

Errno::ETIMEDOUT: 操作超时 - connect(2)

问题:

  • 如何定义TCPSocket.new 的超时值?
  • 如何正确捕获超时(或者,通常是套接字)异常?

【问题讨论】:

标签: ruby sockets


【解决方案1】:

至少从 2.0 开始可以简单地使用:

Socket.tcp("www.ruby-lang.org", 10567, connect_timeout: 5) {}

对于旧版本@falstru 的答案似乎是最好的。

【讨论】:

  • 这是一个比 TCPSocket 更新更好的套接字组件。帮助了我的案子。谢谢!
  • 对于那些不明显的人:你实际上并不想要最后的{};那是您放置代码的块,但如果您只想要套接字本身,只需删除该块。见ruby-doc.org/stdlib-2.0.0/libdoc/socket/rdoc/…
  • 这并不能真正解决读取超时的问题?还是我看错了?
  • @bbozo ,我又看了一遍。我相信读取超时取决于您用来从套接字读取的方法调用。不管是阻塞,不管你用s.reads.read_nonblocks.select……我觉得没有read_timeout这么通用的功能。看看NET::HTTPNet::BufferedIO#rbuf_fill 中是如何做到的。
【解决方案2】:

使用begin .. rescue Errno::ETIMEDOUT 捕捉超时:

require 'socket'

begin
  TCPSocket.new('www.example.com', 111)
rescue Errno::ETIMEDOUT
  p 'timeout'
end

要捕获任何套接字异常,请改用SystemCallError

根据SystemCallError documentation

SystemCallError 是所有低级平台相关错误的基类。

当前平台上可用的错误是 SystemCallError 并在 Errno 模块中定义。


TCPSocket.new 不直接支持超时。

使用Socket::connect_non_blockingIO::select 设置超时。

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)

以上代码来自“Setting a Socket Connection Timeout in Ruby”。

【讨论】:

    【解决方案3】:

    如果您喜欢避免使用the pitfalls of Timeout 的想法,但希望避免自己处理*_nonblock+select 的实现,您可以使用the tcp_timeout gem

    tcp_timeout gem 猴子补丁 TCPSocket#connect、#read 和 #write,以便它们使用非阻塞 I/O 并具有可以启用的超时。

    【讨论】:

    • tcp_timeout 库很棒,在 ruby​​ 1.9.3 中工作
    【解决方案4】:

    您可以使用 ruby​​ 的 Timeout 模块进行超时:

    reqiure 'socket'
    reqiure 'timeout'
    
    begin 
       Timeout.timeout(10) do
          begin
             TCPSocket.new('www.example.com', 111)
          rescue Errno::ENETUNREACH
             retry # or do something on network timeout
          end
       end
    rescue Timeout::Error
       puts "timed out"
       # do something on timeout
    end
    

    你会在 10 秒后得到:

    # timed out
    # => nil
    

    【讨论】:

    • 这是一个非常简单但有效的解决方案。它应该被投票反对所有套接字复杂性......! +1
    • 这是一个非常危险的解决方案,应该避免,如in this article所述。
    猜你喜欢
    • 2011-02-17
    • 2017-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多