Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
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] Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.
показана наша функция 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).
11-15
Вызывается функция connect
, и если функция прерывается ( EINTR
), мы присваиваем переменной errno значение ETIMEDOUT
. Сокет закрывается, чтобы не допустить продолжения трехэтапного рукопожатия.
16-18
Таймер при обнулении выключается, и восстанавливается предыдущий обработчик сигналов (если таковой имеется).
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
.
Тайм-аут для функции recvfrom (сигнал SIGALRM)
В листинге 14.2 показана новая версия функции dg_cli
, приведенной в листинге 8.4, в которую добавлен вызов функции alarm
для прерывания функции recvfrom при отсутствии ответа в течение 5 с.
Листинг 14.2. Функция dg_cli, в которой при установке тайм-аута для функции recvfrom используется функция alarm
//advio/dgclitimeo3.c
1 #include "unp.h"
2 static void signalrm(int);
3 void
4 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 else
16 err_sys("recvfrom error");
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 }
8-22
Мы устанавливаем обработчик для сигнала SIGALRM
и затем вызываем функцию alarm
для 5-секундного тайм-аута при каждом вызове функции recvfrom
. Если функция recvfrom
прерывается нашим обработчиком сигнала, мы выводим сообщение об ошибке и продолжаем работу. Если получена строка от сервера, мы отключаем функцию alarm и выводим ответ.
24-28
Наш обработчик сигналов возвращает управление, прерывая блокированную функцию recvfrom
.
Этот пример работает корректно, потому что каждый раз, когда мы устанавливаем функцию alarm
, мы читаем только один ответ. В разделе 20.4 мы попытаемся использовать ту же технологию, но поскольку мы будем считывать множество ответов для данной функции alarm
, возникнет ситуация гонок, которую нам придется разрешить.
Интервал:
Закладка: