簡體   English   中英

在給定超時后從 Ruby 的 TCPSocket::gets 逃脫的最佳方法是什么?

[英]What's the best way to escape from Ruby's TCPSocket::gets after a given timeout?

我有以下(簡化的)ruby 代碼,它打開 ruby 的 TCP-Socket 並從中讀取:

socket = TCPSocket.open(server, port)

while line = socket.gets
  line = line.chop
end

現在我只想在給定的時間段(即 1 分鍾)內從套接字讀取數據。 所以在 1 分鍾后,while 塊應該被打破並且進程應該退出。
把一行像

break if (elapsed_time > 1000)

進入gets塊是不可能的,因為如果沒有任何內容寫入套接字,則不會到達這行代碼。

謝謝

要以非阻塞方式從 IO 讀取數據,可以使用read_nonblock 如果 IO 未准備好讀取,它要么讀取可用字節(達到給定的最大值),要么引發異常。 異常解救后,可以調用IO.select等待IO准備好讀取。

IO.select最多占用 3 個 arrays 和 IOs 來監視 1) 讀取,2) 寫入和 3) 異常(您可以一次監視多個 IOs)。 一個基本的循環看起來像這樣:

buffer = String.new

loop do
  begin
    buffer << socket.read_nonblock(1024)
  rescue IO::WaitReadable
    IO.select([socket])
  end
end

以上嘗試讀取最多 1024 個字節,這些字節將被附加到buffer 如果讀取成功,它將讀取接下來的 1024 個字節,依此類推。 如果由於套接字沒有任何數據而導致讀取失敗,它會調用IO.select來監視套接字並在有更多數據可用時立即返回。

IO.select也需要第 4 個參數timeout 如果超過給定值(以秒為單位),它將返回nil ,可用於有條件地break循環:

loop do
  begin
    buffer << socket.read_nonblock(1024)
  rescue IO::WaitReadable
    break unless IO.select([socket], nil, nil, 10)
  end
end

以上將等待最多 10 秒,以便更多數據可用,否則會中斷循環。

但是,每次(失敗的)讀取嘗試都會等待 10 秒。 要獲得整個循環的“全局”超時,您可能需要遵循以下原則:

timeout = 10
buffer = String.new

t = Time.now
remaining = timeout

while remaining > 0
  begin
    buffer << socket.read_nonblock(1024)
  rescue IO::WaitReadable
    break unless IO.select([socket], nil, nil, remaining)
  end

  elapsed = Time.now - t
  remaining = timeout - elapsed
end

上面跟蹤剩余時間(以秒為單位)並將該值作為超時傳遞給IO.select

最后,您可能希望在行可用時立即對其進行處理。 為此,您可以檢查字符串緩沖區中的換行符並通過slice! (可能在一個循環中,因為您可能一次閱讀了多行)。 然后可以生成每條提取的行:

def gets_while(io, timeout)
  buffer = String.new
  
  t = Time.now
  remaining = timeout
  
  while remaining > 0
    begin
      buffer << io.read_nonblock(1024)
      while (newline = buffer.index("\n"))
        yield buffer.slice!(0..newline)
      end
    rescue IO::WaitReadable
      break unless IO.select([io], nil, nil, remaining)
    rescue EOFError
      break # end of stream / connection closed
    end
  
    elapsed = Time.now - t
    remaining = timeout - elapsed
  end
end

用法示例:

socket = TCPSocket.open(server, port)

gets_while(socket, 60) do |line|
  l = line.chomp
  # ...
end

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM