Date: Tue, 05 Nov 2002 19:10:21 +0500
From: Andrey Sapozhnikov <sapa@icb.chel.su>
Newsgroups: ftn.ru.perl
Subject: Задание таймаута в Perl модуле Socket
> Для проверки доступности порта на удаленном хосте я использую
> модуль Socket. Hо мне хотелось бы задать таймаут на установление исходящего
> соединения TCP. Однако, ни среди параметров функции connect(), ни среди опций
> сокета, устанавливаемых посредством setsocktopt(), я не обнаружил параметра,
> определяющего таймаут на установление соединения.
>
> Предусмотрен ли вообще такой параметр?
> Если ли нет, то каким образом можно установить требуемый таймаут, кроме
> очевидного решения с применением $SIG{ALRM}?
Гораздо лучше реализовать подобный таймаут с помощью select.
use strict;
use Socket;
use Fcntl;
use Errno;
my $proto = getprotobyname('tcp');
socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
my $sin = sockaddr_in(80, inet_aton('192.168.0.1'));
# bind можно опустить, тогда будет неявно вызван
# bind(SOCK, sockaddr_in(0, INADDR_ANY))
# Теперь переводим сокет в non-block mode
fcntl(SOCK, F_SETFL, O_NONBLOCK) or die "fcntl: $!";
unless (connect(SOCK, $sin)) {
# Hекоторые системы возвращают EWOULDBLOCK вместо EINPROGRESS
Errno::EINPROGRESS == $! or Errno::EWOULDBLOCK or
die "connect: $!";
vec(my $win = '', fileno(SOCK), 1) = 1;
# Таймаут соединения - десять с половиной секунд
unless (select(undef, $win, undef, 10.5)) {
close (SOCK);
die "Time is out!n";
}
if (defined (my $ret = getsockopt(SOCK, SOL_SOCKET, SO_ERROR))) {
die "connection failed: $!" if $! = unpack('i', $ret);
# Hе во всех системах поддерживается SO_ERROR
# fallback до getpeername
} elsif (!getpeername(SOCK)) {
die "connection failed: $!";
}
}
fcntl(SOCK, F_SETFL, 0);
Если требуется что-то после этого читать-писать из
сокета, и конкретный тип ошибки соединения не важен,
то можно выбросить все проверки на ошибки после select,
и ориентироваться на то, что вернет read или write. При
этом надо иметь в виду, что в некоторых системах запись
в неоткрытый сокет вызовет сигнал SIGPIPE котоый нужно
будет обработать или игнорировать. Если писать ничего
не требуется, то можно сэкономить последнюю строчку
и не снимать O_NONBLOCK :-) А в общем, я рекомендовал
бы именно так.