在 ruby​​ 套接字上实现超时的最佳方法

发布于 2024-12-04 06:17:57 字数 1798 浏览 0 评论 0原文

我只是一名机械工程师(编程新手),正在开发一个用于监控客户太阳能系统功率输出的 Web 应用程序。我目前正在尝试实现我的应用程序用来打开套接字连接并使用超时和线程查询客户逆变器的脚本。目前,该脚本(通过 cron 设置为每 15 分钟运行一次)非常简单,看起来像这样:

System.all.each do |system|
  @sock = TCPSocket.open(system.ip,51101)
  @query = "\x0A\x05\x03\x00\xB5\x00\x01\x94\x68\x0D"
  @sock.write(@query)      
  @s = @sock.read(9)
  # do stuff with @s
  @sock.close
end

我首先要解决的大问题之一是:每天日落之后,所有客户逆变器都会关闭一天,这个脚本尝试它的第一次 socket.write/read,我相信它“挂起”,因为为了让它再次开始连接和查询,我必须在早上重新启动我的计算机。所以我想在套接字本身和 socket.read 上正确实现超时,这样就不会发生这种情况。从我到目前为止所做的堆栈溢出研究来看,我想也许我应该做如下的事情来完成与上面相同的事情,但是套接字在 2 秒后超时:

@port = 51101
System.all.each do |system|
  @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
  @sock_address = Socket.sockaddr_in(@port, system.ip)

  begin
    @sock.connect_nonblock(@sock_address)
    @query = "\x0A\x05\x03\x00\xB5\x00\x01\x94\x68\x0D"
    @sock.write(@query)      
    @s = @sock.read(9)
    # do stuff with @s
    @sock.close
  rescue Errno::EINPROGRESS
    if IO.select(nil, [@sock], nil, 2)
      begin
        @sock.connect_nonblock(@sock_address)
        @query = "\x0A\x05\x03\x00\xB5\x00\x01\x94\x68\x0D"
        @sock.write(@query)      
        @s = @sock.read(9)
        # do stuff with @s
        @sock.close
      rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
        # do something
      end
    end
  end
end

但是有很多事情我不这样做不明白这段代码。首先,Errno::EINPROGRESS 何时引发?第二个 @sock.connect_nonblock(@sock_address) 是否出现在第 6 行,因为如果系统已经尝试“太长时间”但不超过 2 秒,此代码将重试连接?另外,从我读到的 Errno::EINPROGRESS 似乎是 Windows 的事情。 Mac 操作系统的等效项是什么?或者这些问题可能完全不是重点。如果我的方向正确,请告诉我。或者您是否可以向我提供一个带注释的片段,演示执行此操作的正确方法。

其次,如果套接字连接得很好,我怎样才能实现 socket.read 的超时,以免一切都挂起(我认为这就是逆变器关闭时实际发生的情况)?我一直读到不应使用 ruby​​ timeout.rb 。我怎样才能让它只尝试读取几秒钟,然后在挂起时关闭套接字?预先非常感谢您。

I am just a mechanical engineer (a newbie to programming) developing a web application for monitoring the power output of customer solar systems. I am currently trying to implement the script my application uses to open a socket connection with and query customer inverters using timeouts and threads. Currently the script (which is set via a cron to run every 15 minutes) is very trivial and looks something like this:

System.all.each do |system|
  @sock = TCPSocket.open(system.ip,51101)
  @query = "\x0A\x05\x03\x00\xB5\x00\x01\x94\x68\x0D"
  @sock.write(@query)      
  @s = @sock.read(9)
  # do stuff with @s
  @sock.close
end

One of the big problems I want to solve before anything else: after sunset every day when all customer inverters shut down for the day and this script attempts its first socket.write/read, I believe it "hangs" because in order for it to start connecting and querying again, I have to restart my computer in the morning. So I'd like to implement timeouts properly both on the socket itself and also on socket.read so that this doesn't happen. From the stack overflow research I've done so far, I thought maybe I should do something like the following to do the same thing as above, but with a socket that times out after 2 seconds:

@port = 51101
System.all.each do |system|
  @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
  @sock_address = Socket.sockaddr_in(@port, system.ip)

  begin
    @sock.connect_nonblock(@sock_address)
    @query = "\x0A\x05\x03\x00\xB5\x00\x01\x94\x68\x0D"
    @sock.write(@query)      
    @s = @sock.read(9)
    # do stuff with @s
    @sock.close
  rescue Errno::EINPROGRESS
    if IO.select(nil, [@sock], nil, 2)
      begin
        @sock.connect_nonblock(@sock_address)
        @query = "\x0A\x05\x03\x00\xB5\x00\x01\x94\x68\x0D"
        @sock.write(@query)      
        @s = @sock.read(9)
        # do stuff with @s
        @sock.close
      rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
        # do something
      end
    end
  end
end

But there are a number of things I don't understand about this code. Firstly, when is Errno::EINPROGRESS raised? Does the second @sock.connect_nonblock(@sock_address) appear in line 6, because this code would retry the connection if the system had already been trying for "too long" but not over 2 seconds? Also from what I read Errno::EINPROGRESS seems to be a Windows thing. What would be the Mac OS equivalent? Or these questions could be totally beside the point. Please let me know either way if I'm going in the right direction. Or if you could provide me with a commented snippet demonstrating the right way to do this.

Secondly, if the socket connects just fine, how could I implement a timeout of the socket.read so that it doesn't make everything hang (which is what I think is actually happening when the inverters shut down)? I keep reading that the ruby timeout.rb should not be used. How could I make it only try reading for a few seconds and then close the socket if it hangs? Thank you so much in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

转身泪倾城 2024-12-11 06:17:57

据我现在的了解,最简单的(不一定是最好的——取决于您的需求)只是为了避免尝试让我的套接字连接尝试超时以及我的 socket.read 超时的问题。由于我的脚本很简单并且我使用的是 Ruby 1.8.7,因此我使用的是 system_timer gem(ruby 的超时库对于 Ruby 1.8.x 来说并不可靠,而 system_timer 解决了 Ruby 1.8.x 的超时问题)。

http://systemtimer.rubyforge.org/

该文档很好。

From what I understand now, it is easiest (not necessarily best--depends on your needs) just to avoid this problem of trying to have my socket connection attempt time out as well as my socket.read time out. Since my script is simple and I am using Ruby 1.8.7, I am using the system_timer gem (ruby's timeout library is not reliable for Ruby 1.8.x and system_timer gets around timeout's issues for Ruby 1.8.x).

http://systemtimer.rubyforge.org/

The documentation is good.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文