Есть два способа прерывания connect по тайм-ауту. Самый простой - окружить этот вызов обращениями к alarm. Предположим, например, что вы не хотите ждать завершения connect более пяти секунд. Тогда можно модифицировать каркас tcpclient. skel (листинг 2.6), добавив простой обработчик сигнала и немного видоизменив функцию main:
void alarm_hndlr (int sig)
{
return;
}
int main ( int argc, char **argv )
{
…
signal ( SIGALRM, alarm_hndlr );
alarm( 5 );
rc = connect(s, ( struct sockaddr * )&peer, sizeof( peer ) )
alarm( 0 );
if ( rc < 0 )
{
if ( errno == EINTR )
error( 1, 0, "истек тайм-аут connect\n" );
…
}
Назовем программу, созданную по этому каркасу, connecto и попытаемся с помощью соединиться с очень загруженным Web-сервером Yahoo. Получите ожидаемый результат:
bsd: $ connectto yahoo.com daytime
connectto: истек тайм-аут connect спустя 5 с
bsd: $
Хотя это и простое решение, с ним связано две потенциальных проблемы. Сначала обсудим их, а потом рассмотрим другой метод - он сложнее, но лишен этих недостатков.
Прежде всего в данном примере подразумевается, что «тревожный» таймер, используемый в вызове alarm, нигде в программе не применяется, и, значит, для сигнала SIGALRM не установлен другой обработчик. Если таймер уже взведен где-то еще, то приведенный код его переустановит, поэтому старый таймер не сработает. Правильнее было бы сохранить и затем восстановить время, оставшееся до срабатывания текущего таймера (его возвращает вызов alarm), а также сохранить и восстановить текущий обработчик сигнала SIGALRM (его адрес возвращает вызов signal). Чтобы все было корректно, надо было также получить время, проведенное в вызове connect, и вычесть его из времени, оставшегося до срабатывания исходного таймера.
Далее, для упрощения вы завершаете клиент, если connect не вернул управления вовремя. Вероятно, нужно было бы предпринять иные действия. Однако надо иметь в виду, что перезапустить connect нельзя. Дело в том, что в результате вызова connect сокет остался привязанным к ранее указанному адресу, так что попытка повторного выполнения приведет к ошибке «Address already in use». При желании повторить connect, возможно, немного подождав, придется сначала закрыть, а затем заново открыть сокет, вызвав close (или closesocket) и socket.