Чтение онлайн

ЖАНРЫ

UNIX: разработка сетевых приложений
Шрифт:

17 } else {

18 alarm(0);

19 recvline[n] = 0; /* завершающий нуль */

20 Fputs(recvline, stdout);

21 }

22 }

23 }

24 static void

25 sig_alrm(int signo)

26 {

27 return; /* просто прерываем recvfrom */

28 }

Обработка
тайм-аута из функции recvfrom

8-22
Мы устанавливаем обработчик для сигнала
SIGALRM
и затем вызываем функцию
alarm
для 5-секундного тайм-аута при каждом вызове функции
recvfrom
. Если функция
recvfrom
прерывается нашим обработчиком сигнала, мы выводим сообщение об ошибке и продолжаем работу. Если получена строка от сервера, мы отключаем функцию alarm и выводим ответ.

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

24-28
Наш обработчик сигналов возвращает управление, прерывая блокированную функцию
recvfrom
.

Этот пример работает корректно, потому что каждый раз, когда мы устанавливаем функцию

alarm
, мы читаем только один ответ. В разделе 20.4 мы попытаемся использовать ту же технологию, но поскольку мы будем считывать множество ответов для данной функции
alarm
, возникнет ситуация гонок, которую нам придется разрешить.

Тайм-аут для функции recvfrom (функция select)

Мы демонстрируем вторую технологию для установки тайм-аута (использование функции

select
) в листинге 14.3. Здесь показана наша функция
readable_timeo
, которая ждет, когда дескриптор станет готов для чтения, но не более заданного числа секунд.

Листинг 14.3. Функция readable_timeo: ожидание, когда дескриптор станет готов для чтения

//lib/readable_timео.c

1 #include "unp.h"

2 int

3 readable_timeo(int fd, int sec)

4 {

5 fd_set rset;

6 struct timeval tv;

7 FD_ZERO(&rset);

8 FD_SET(fd, &rset);

9 tv.tv_sec = sec;

10 tv.tv_usec = 0;

11 return (select(fd + 1, &rset, NULL, NULL, &tv));

12 /* > если дескриптор готов для чтения */

13 }

Подготовка аргументов для функции select

7-10
В наборе дескрипторов для чтения включается бит, соответствующий данному дескриптору. В структуре
timeval
устанавливается время (число секунд), в течение которого вызывающий процесс готов ждать.

Блокирование в функции select

11-12
Функция
select
ждет, когда дескриптор станет готов для чтения или истечет заданное время ожидания. Возвращаемое значение этой функции —
это возвращаемое значение функции
select
: -1 в случае ошибки, 0, если истекло время ожидания, и положительное значение, задающее число готовых дескрипторов, если таковые появились.

Эта функция не выполняет операции чтения — она просто ждет, когда дескриптор будет готов к чтению. Следовательно, эту функцию можно использовать с любым типом сокета — TCP или UDP.

Создание аналогичной функции, называемой

writable_timeo
, тривиально. Эта функция ждет, когда дескриптор будет готов для записи.

Мы используем эту функцию в листинге 14.4, где показана еще одна версия нашей функции

dg_cli
, приведенной в листинге 8.4. Эта новая версия вызывает функцию
recvfrom
, только когда наша функция
readable_timeo
возвращает положительное значение.

Мы не вызываем функцию

recvfrom
, пока функция
readable_timeo
не сообщит нам, что дескриптор готов для чтения. Тем самым мы гарантируем, что функция
recvfrom
не заблокируется.

Листинг 14.4. Функция dg_cli, вызывающая функцию readable_timeo для установки тайм-аута

//advio/dgclitimeo1.c

1 #include "unp.h"

2 void

3 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)

4 {

5 int n;

6 char sendline[MAXLINE], recvline[MAXLINE + 1];

7 while (Fgets(sendline, MAXLINE, fp) != NULL) {

8 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);

9 if (Readable_timeo(sockfd, 5) == 0) {

10 fprintf(stderr, "socket timeout\n");

11 } else {

12 n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);

13 recvline[n] = 0; /* завершающий нуль */

14 Fputs(recvline, stdout);

15 }

16 }

17 }

Тайм-аут для функции recvfrom (параметр сокета SO_RCVTIMEO)

В нашем последнем примере демонстрируется применение параметра сокета

SO_RCVTIMEO
. Мы устанавливаем этот параметр один раз для дескриптора, задавая значение тайм-аута, и этот тайм-аут затем применяется ко всем операциям чтения этого дескриптора. Одна из замечательных особенностей этого метода состоит в том, что мы устанавливаем данный параметр только один раз, тогда как предыдущие два метода требовали выполнения некоторых действий перед каждой операцией, для которой мы хотели задать временной предел. Но этот параметр сокета применяется только к операциям чтения. Аналогичный параметр
SO_SNDTIMEO
применяется только к операциям записи, и ни один параметр сокета не может использоваться для установки тайм-аута для функции
connect
.

Поделиться с друзьями: