Как правильно использовать тайм-аут получения для сокетов Ruby? - PullRequest
1 голос
/ 22 февраля 2012

Блокировка сокетов Беркли, обычно используемых Ruby, имеет один недостаток: если соединение с удаленным компьютером не завершено изящно, вызов recv() будет ждать часами.Обычно это можно исправить, указав таймаут приема для сокета.Я пробовал следующий код с Ruby 1.9.3-p0 на Windows 7 и Ubuntu 11.10.К сожалению, это не работает: recv() зависает навсегда :(. Что я делаю не так?

# server.rb
require 'socket'
Socket.tcp_server_loop( 1234 ) { puts( "connected" ) }

# client.rb
require 'socket'
sock = Socket.new( :INET, :STREAM )
time = if RUBY_PLATFORM =~ /mingw/ then 1000 else [ 1, 0 ].pack( 'L*' ) end
sock.setsockopt( Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, time )
sock.connect( Socket.sockaddr_in( 1234, "127.0.0.1" ) )
sock.recv( 100 )

1 Ответ

2 голосов
/ 22 февраля 2012

Я не уверен, что вы пытаетесь сделать, но сервер никогда ничего не пишет. Кроме того, из-за способа использования tcp_server_loop сокет никогда не закрывается, а выходит из области видимости, что означает, что он будет закрыт только при следующей сборке мусора. Поскольку вы слушаете данные, которые никогда не поступают в сокет, который никогда не был закрыт на стороне сервера, это должно иметь какое-то отношение к этому. Попробуйте это для сервера:

Socket.tcp_server_loop(1234) do |sock, clientinfo|
  puts "Connection!"
  begin
    sock.puts "Welcome to Wonderland"
  ensure
    sock.close
  end
end

Что касается времени, в Linux я могу только догадываться, что это вопрос заполнения. Я думаю, что вы представляете себе идеальную последовательную структуру, которой не существует. Вы должны действительно использовать только двоичные структуры, которые были возвращены из функций Си. И если вы знаете, что опция работает в C / C ++, вы можете написать простое расширение, которое просто определяет одну функцию Ruby, которая устанавливает сокет, устанавливает эту опцию и возвращает ее. Это, вероятно, сложнее, чем кажется, но это надежный вариант.

EDIT:

Вы можете использовать библиотеку времени ожидания Ruby и создать время ожидания вызова recv. Теперь это будет выглядеть так:

require 'socket'
require 'timeout'
sock = Socket.new( :INET, :STREAM )
time = if RUBY_PLATFORM =~ /mingw/ then 1000 else [ 1, 0 ].pack( 'L*' ) end
sock.setsockopt( Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, time )
sock.connect( Socket.sockaddr_in( 1234, "127.0.0.1" ) )
(Timeout.timeout(100) {sock.recv( 100 )}) rescue puts("Recv timed out")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...