Литмир - Электронная Библиотека
Содержание  
A
A

Мы также рассказываем о том, как определить, сколько данных находится в приемном буфере сокета и как использовать с сокетами стандартную библиотеку ввода-вывода С, и обсуждаем более совершенные способы ожидания событий.

14.2. Тайм-ауты сокета

Существует три способа установки тайм-аута для операции ввода-вывода через сокет.

1. Вызов функции

alarm
, которая генерирует сигнал
SIGALRM
, когда истекает заданное время. Это подразумевает обработку сигналов, которая может варьироваться от одной реализации к другой. К тому же такой подход может стать помехой другим существующим вызовам функции
alarm
в данном процессе.

2. Блокирование при ожидании ввода-вывода в функции

select
, имеющей встроенное ограничение времени, вместо блокирования в вызове функции
read
или
write
.

3. Использование более новых параметров сокета —

SO_RCVTIMEO
и
SO_SNDTIMEO
. Проблема при использовании этого подхода заключается в том, что не все реализации поддерживают новые параметры сокетов.

Все три технологии работают с функциями ввода и вывода (такими как

read
,
write
и их вариациями, например
recvfrom
и
sendto
), но нам также хотелось бы иметь технологию, работающую с функцией
connect
, поскольку процесс соединения TCP может занять длительное время (обычно 75 с). Функцию
select
можно использовать для установки тайм-аута функции
connect
, только когда сокет находится в неблокируемом режиме (который мы рассматриваем в разделе 16.3), а параметры сокетов, устанавливающие тайм-аут, не работают с функцией
connect
. Мы также должны отметить, что первые две технологии работают с любым дескриптором, в то время как третья технология только с дескрипторами сокетов.

Теперь мы представим примеры применения всех трех технологий.

Тайм-аут для функции connect (сигнал SIGALRM)

В листинге 14.1[1] показана наша функция

connect_timeo
, вызывающая функцию connect с ограничением по времени, заданным вызывающим процессом. Первые три аргумента — это аргументы, которых требует функция
connect
, а четвертый — это длительность ожидания в секундах.

Листинг 14.1. Функция connect с тайм-аутом

//lib/connect_timeo.c

 1 #include "unp.h"

 2 static void connect_alarm(int);

 3 int

 4 connect_timeo(int sockfd, const SA *saptr, socklen_t salen, int nsec)

 5 {

 6  Sigfunc *sigfunc;

 7  int n;

 8  sigfunc = Signal(SIGALRM, connect_alarm);

 9  if (alarm(nsec) != 0)

10   err_msg("connect_timeo: alarm was already set");

11  if ((n = connect(sockfd, saptr, salen)) < 0) {

12   close(sockfd);

13   if (errno == EINTR)

14    errno = ETIMEDOUT;

15  }

16  alarm(0); /* отключение alarm */

17  Signal(SIGALRM, sigfunc); /* восстанавливаем прежний обработчик

                                 сигнала */

18  return (n);

19 }

20 static void

21 connect_alarm(int signo)

22 {

23  return; /* просто прерываем connect() */

24 }

Установка обработчика сигналов

8
 Для
SIGALRM
устанавливается обработчик сигнала. Текущий обработчик сигнала (если таковой имеется) сохраняется, и таким образом мы можем восстановить его в конце функции.

Установка таймера

9-10
 Таймер для процесса устанавливается на время (число секунд), заданное вызывающим процессом. Возвращаемое значение функции
alarm
— это число секунд, остающихся в таймере для процесса (если он уже установлен для процесса) в настоящий момент или 0 (если таймер не был установлен прежде). В первом случае мы выводим сообщение с предупреждением, поскольку мы стираем предыдущую установку таймера (см. упражнение 14.2).

Вызов функции connect

11-15
 Вызывается функция
connect
, и если функция прерывается (
EINTR
), мы присваиваем переменной errno значение
ETIMEDOUT
. Сокет закрывается, чтобы не допустить продолжения трехэтапного рукопожатия.

Выключение таймера и восстановление предыдущего обработчика сигнала

16-18
 Таймер при обнулении выключается, и восстанавливается предыдущий обработчик сигналов (если таковой имеется).

Обработка сигнала SIGALRM

20-24
 Обработчик сигнала просто возвращает управление. Предполагается, что это прервет ожидание функции
connect
, заставив ее возвратить ошибку
EINTR
. Вспомните нашу функцию
signal
(см. листинг 5.5), которая не устанавливает флага
SA_RESTART
, когда перехватываемый сигнал — это сигнал
SIGALRM
.

Одним из важных моментов в этом примере является то, что мы всегда можем сократить период ожидания для функции

connect
, используя эту технологию, но мы не можем увеличить период, заданный для ядра. В Беркли-ядре тайм-аут для функции
connect
обычно равен 75 с. Мы можем задать меньшее значение для нашей функции, допустим 10, но если мы задаем большее значение, скажем 80, тайм- аут самой функции
connect
все равно составит 75 с.

Другой важный момент в данном примере — то, что мы используем возможность прерывания системного вызова (

connect
) для того, чтобы возвратить управление, прежде чем истечет время ожидания ядра. Такой подход допустим, когда мы выполняем системный вызов и можем обработать возвращение ошибки
EINTR
. Но в разделе 29.7 мы встретимся с библиотечной функцией, выполняющей системный вызов, которая сама выполняет заново системный вызов при возвращении ошибки
EINTR
. Мы можем продолжать работать с сигналом
SIGALRM
и в этом случае, но в листинге 29.6 мы увидим, что нам придется воспользоваться функциями
sigsetjmp
и
siglongjmp
, поскольку библиотечная функция игнорирует ошибку
EINTR
.

вернуться

1

Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.

154
{"b":"225366","o":1}