Я думаю, что вам в основном не повезло. Когда я запускаю ваш пример с strace
(используя только внешний сервер для поддержания чистоты вывода), легко проверить, что setsockopt
действительно вызывается:
$ strace -f ruby foo.rb 2>&1 | grep setsockopt
[pid 5833] setsockopt(5, SOL_SOCKET, SO_RCVTIMEO, "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) = 0
strace
также показывает, что блокирует программу. Это строка, которую я вижу на экране до истечения времени ожидания сервера:
[pid 5958] ppoll([{fd=5, events=POLLIN}], 1, NULL, NULL, 8
Это означает, что программа блокирует этот вызов на ppoll
, а не на вызов recvfrom
. Страница руководства со списком параметров сокетов ( socket (7) ) гласит:
Тайм-ауты не влияют на выбор (2), опрос (2), epoll_wait (2) и т. Д.
Таким образом, время ожидания устанавливается, но не оказывает влияния. Надеюсь, я ошибаюсь, но, похоже, в Ruby нет способа изменить это поведение. Я быстро взглянул на реализацию и не нашел очевидного выхода. Опять же, я надеюсь, что я не прав - кажется, это что-то базовое, почему это не так?
Один (очень уродливый) обходной путь - использование dl
для прямого вызова read
или recvfrom
. На эти звонки влияет установленное вами время ожидания. Например:
require 'socket'
require 'dl'
require 'dl/import'
module LibC
extend DL::Importer
dlload 'libc.so.6'
extern 'long read(int, void *, long)'
end
sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
timeval = [3, 0].pack("l_l_")
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeval
sock.connect( Socket.pack_sockaddr_in(1234, '127.0.0.1'))
buf = "\0" * 1024
count = LibC.read(sock.fileno, buf, 1024)
if count == -1
puts 'Timeout'
end
Этот код работает здесь. Конечно, это уродливое решение, которое не будет работать на многих платформах и т. Д. Однако, это может быть выход.
Также обратите внимание, что это первый раз, когда я делаю что-то подобное в Ruby, поэтому я не знаю обо всех подводных камнях, которые я могу пропустить - в частности, я подозреваю типы, которые я указал в 'long read(int, void *, long)'
и, кстати, я передаю буфер для чтения.