UNIX: разработка сетевых приложений
Тайм-аут для функции recvfrom (сигнал SIGALRM)
В листинге 14.2 показана новая версия функции
, приведенной в листинге 8.4, в которую добавлен вызов функцииdg_cliдля прерывания функции recvfrom при отсутствии ответа в течение 5 с.alarmЛистинг 14.2. Функция dg_cli, в которой при установке тайм-аута для функции recvfrom используется функция alarm
//advio/dgclitimeo3.c1 #include "unp.h"2 static void signalrm(int);3 void4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)5 {6 int n;7 char sendline[MAXLINE], recvline[MAXLINE + 1];8 Signal(SIGALRM, signalrm);9 while (Fgets(sendline, MAXLINE, fp) != NULL) {10 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);11 alarm(5);12 if ((n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) {13 if (errno == EINTR)14 fprintf(stderr, "socket timeout\n");15 else16 err_sys("recvfrom error");17 } else {18 alarm(0);19 recvline[n] = 0; /* завершающий нуль */20 Fputs(recvline, stdout);21 }22 }23 }24 static void25 sig_alrm(int signo)26 {27 return; /* просто прерываем recvfrom() */28 }Обработка тайм-аута из функции recvfromМы устанавливаем обработчик для сигнала8-22и затем вызываем функциюSIGALRMдля 5-секундного тайм-аута при каждом вызове функцииalarm. Если функцияrecvfromпрерывается нашим обработчиком сигнала, мы выводим сообщение об ошибке и продолжаем работу. Если получена строка от сервера, мы отключаем функцию alarm и выводим ответ.recvfromОбработчик сигнала SIGALRMНаш обработчик сигналов возвращает управление, прерывая блокированную функцию24-28.recvfromЭтот пример работает корректно, потому что каждый раз, когда мы устанавливаем функцию
, мы читаем только один ответ. В разделе 20.4 мы попытаемся использовать ту же технологию, но поскольку мы будем считывать множество ответов для данной функцииalarm, возникнет ситуация гонок, которую нам придется разрешить.alarmТайм-аут для функции recvfrom (функция select)
Мы демонстрируем вторую технологию для установки тайм-аута (использование функции
) в листинге 14.3. Здесь показана наша функцияselect, которая ждет, когда дескриптор станет готов для чтения, но не более заданного числа секунд.readable_timeoЛистинг 14.3. Функция readable_timeo: ожидание, когда дескриптор станет готов для чтения
//lib/readable_timео.c1 #include "unp.h"2 int3 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: -1 в случае ошибки, 0, если истекло время ожидания, и положительное значение, задающее число готовых дескрипторов, если таковые появились.selectЭта функция не выполняет операции чтения — она просто ждет, когда дескриптор будет готов к чтению. Следовательно, эту функцию можно использовать с любым типом сокета — TCP или UDP.
Создание аналогичной функции, называемой
, тривиально. Эта функция ждет, когда дескриптор будет готов для записи.writable_timeoМы используем эту функцию в листинге 14.4, где показана еще одна версия нашей функции
, приведенной в листинге 8.4. Эта новая версия вызывает функциюdg_cli, только когда наша функцияrecvfromвозвращает положительное значение.readable_timeoМы не вызываем функцию
, пока функцияrecvfromне сообщит нам, что дескриптор готов для чтения. Тем самым мы гарантируем, что функцияreadable_timeoне заблокируется.recvfromЛистинг 14.4. Функция dg_cli, вызывающая функцию readable_timeo для установки тайм-аута
//advio/dgclitimeo1.c