Лучший способ реализации таймаутов на рубиновых сокетах - PullRequest
1 голос
/ 13 сентября 2011

Я всего лишь инженер-механик (новичок в программировании), разрабатывающий веб-приложение для контроля выходной мощности солнечных систем клиентов.В настоящее время я пытаюсь реализовать сценарий, который используется моим приложением для открытия соединения с сокетом и запроса пользовательских преобразователей с использованием таймаутов и потоков.В настоящее время скрипт (который устанавливается через 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 OS?Или эти вопросы могут быть совершенно неуместны.Пожалуйста, дайте мне знать в любом случае, если я иду в правильном направлении.Или, если бы вы могли предоставить мне откомментированный фрагмент, демонстрирующий правильный способ сделать это.

Во-вторых, если сокет подключается очень хорошо, как я могу реализовать таймаут socket.read, чтобы он не заставлял все зависать (что, на мой взгляд, происходит на самом деле, когда инверторы выключаются)?Я продолжаю читать, что ruby ​​timeout.rb не должен использоваться.Как я могу сделать это только попытаться прочитать в течение нескольких секунд, а затем закрыть сокет, если он зависает?Заранее большое спасибо.

1 Ответ

1 голос
/ 14 сентября 2011

Из того, что я понимаю сейчас, проще всего (не обязательно лучше - зависит от ваших потребностей) просто избежать этой проблемы: попытаться сократить время попытки подключения к сокету, а также время ожидания сокета. Поскольку мой сценарий прост и я использую Ruby 1.8.7, я использую гем system_timer (библиотека времени ожидания ruby ​​не надежна для Ruby 1.8.x, а system_timer позволяет обойти проблемы времени ожидания для Ruby 1.8.x).

http://systemtimer.rubyforge.org/

Документация хорошая.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...