Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
1 #include "unp.h"
2 static void recvfrom_alarm(int);
3 void
4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
5 {
6 int n;
7 const int on = 1;
8 char sendline[MAXLINE], recvline[MAXLINE + 1];
9 sigset_t sigset_alrm;
10 socklen_t len;
11 struct sockaddr *preply_addr;
12 preply_addr = Malloc(servlen);
13 Setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
14 Sigemptyset(&sigset_alrm);
15 Sigaddset(&sigset_alrm, SIGALRM);
16 Signal(SIGALRM, recvfrom_alarm);
17 while (Fgets(sendline, MAXLINE, fp) != NULL) {
18 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
19 alarm(5);
20 for (;;) {
21 len = servlen;
22 Sigprocmask(SIG_UNBLOCK, &sigset_alrm, NULL);
23 n = recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);
24 Sigprocmask(SIG_BLOCK, &sigset_alrm, NULL);
25 if (n
26 if (errno == EINTR)
27 break; /* окончание ожидания ответа */
28 else
29 err_sys("recvfrom error");
30 } else {
31 recvline[n] = 0; /* завершающий нуль */
32 printf("from %s: %s",
33 Sock_ntop_host(preply_addr, len), recvline);
34 }
35 }
36 }
37 free(preply_addr);
38 }
39 static void
40 recvfrom_alarm(int signo)
41 {
42 return; /* выход из recvfrom() */
43 }
14-15
Мы объявляем набор сигналов, инициализируем его как пустой набор ( sigemptyset
) и включаем бит, соответствующий сигналу SIGALRM
( sigaddset
).
21-24
Перед вызовом функции recvfrom
мы разблокируем сигнал (с тем, чтобы он мог быть доставлен, пока наша программа блокирована), а затем блокируем его, как только завершается функция recvfrom
. Если сигнал генерируется (истекает время таймера), когда сигнал блокирован, то ядро запоминает этот факт, но доставить сигнал (то есть вызвать наш обработчик) не может, пока сигнал не будет разблокирован. В этом состоит принципиальная разница между генерацией сигнала и его доставкой . В главе 10 [110] предоставлена более подробная информация обо всех аспектах обработки сигналов POSIX.
Если мы откомпилируем и запустим эту программу, нам будет казаться, что она работает нормально, но все программы, порождающие ситуацию гонок, большую часть времени работают без каких-либо проблем! Проблема остается: разблокирование сигнала, вызов функции recvfrom
и блокирование сигнала — все эти действия являются независимыми системными вызовами. Будем считать, что функция recvfrom
возвращает последний ответ на нашу дейтаграмму, а сигнал доставляется между вызовом функции recvfrom
и блокированием сигнала. Следующий вызов функции recvfrom
заблокируется навсегда. Мы ограничили размер окна, но проблема осталась.
Вариантом решения может быть установка глобального флага при доставке сигнала его обработчиком:
recvfrom_alarm(int signo) {
had_alarm = 1;
return;
}
Флаг сбрасывается в 0 каждый раз, когда вызывается функция alarm
. Наша функция dg_cli
проверяет этот флаг перед вызовом функции recvfrom
и не вызывает ее, если флаг ненулевой.
for (;;) {
len = servlen;
Sigprocmask(SIG_UNBLOCK, &sigset_alrm, NULL);
if (had_alarm == 1)
break;
n = recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);
Если сигнал был сгенерирован во время его блокирования (после предыдущего возвращения из функции recvfrom
), то после разблокирования в этой части кода он будет доставлен перед завершением функции sigprocmask
, устанавливающей наш флаг. Однако между проверкой флага и вызовом функции recvfrom
существует промежуток времени, в течение которого сигнал может быть сгенерирован и доставлен, и если это произойдет, вызов функции recvfrom
заблокируется навсегда (разумеется, мы считаем при этом, что не приходит никаких дополнительных ответов).
Блокирование и разблокирование сигнала с помощью функции pselect
Одним из корректных решений будет использование функции pselect
(см. раздел 6.9), как показано в листинге 20.3.
Листинг 20.3. Блокирование и разблокирование сигналов с помощью функции pselect
//bcast/dgclibcast4.с
1 #include "unp.h"
2 static void recvfrom_alarm(int);
3 void
4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
5 {
6 int n;
7 const int on = 1;
8 char sendline[MAXLINE], recvline[MAXLINE + 1];
9 fd_set rset;
10 sigset_t sigset_alrm, sigset_empty;
11 socklen_t len;
12 struct sockaddr *preply_addr;
13 preply_addr = Malloc(servlen);
14 Setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
15 FD_ZERO(&rset);
16 Sigemptyset(&sigset_empty);
17 Sigemptyset(&sigset_alrm);
18 Sigaddset(&sigset_alrm, SIGALRM);
19 Signal(SIGALRM, recvfrom_alarm);
20 while (Fgets(sendline, MAXLINE, fp) != NULL) {
21 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
22 Sigprocmask(SIG_BLOCK, &sigset_alrm, NULL);
23 alarm(5);
24 for (;;) {
25 FD_SET(sockfd, &rset);
26 n = pselect(sockfd + 1, &rset, NULL, NULL, NULL, &sigset_empty);
27 if (n
28 if (errno == EINTR)
29 break;
30 else
31 err_sys("pselect error");
32 } else if (n != 1)
33 err_sys("pselect error; returned %d", n);
34 len = servlen;
35 n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);
36 recvline[n] = 0; /* завершающий нуль */
37 printf("from %s: %s",
38 Sock_ntop_host(preply_addr, len), recvline);
39 }
40 }
41 free(preply_addr);
42 }
43 static void
44 recvfrom_alarm(int signo)
45 {
46 return; /* просто прерываем recvfrom() */
47 }
22-23
Мы блокируем сигнал SIGALRM
и вызываем функцию pselect
. Последний аргумент этой функции — указатель на нашу переменную sigset_empty
, являющуюся набором сигналов, в котором нет блокированных сигналов (все сигналы разблокированы). Функция pselect
сохранит текущую маску сигналов (которая блокирует SIGALRM
), проверит заданные дескрипторы, заблокируется при необходимости с маской сигналов, установленной в пустой набор, но перед завершением функции маска сигналов процесса будет переустановлена в исходное значение, которое она имела при вызове функции pselect
. Ключ к пониманию функции pselect
в том, что установка маски сигналов, проверка дескрипторов и переустановка маски сигнала — это атомарные операции по отношению к вызывающему процессу.
34-38
Если наш сокет готов для чтения, мы вызываем функцию recvfrom
, зная, что она не заблокируется.
Как мы упоминали в разделе 6.9, функция pselect
— относительно новая среди других, описываемых спецификацией POSIX. Из всех систем, показанных на рис. 1.7, эту функцию поддерживают только FreeBSD и Linux. Тем не менее в листинге 20.4 представлена простая, хотя и некорректная ее реализация. Мы приводим ее здесь, несмотря на некорректность, чтобы продемонстрировать три стадии решения: установку маски сигнала в значение, заданное вызывающей функцией, с сохранением текущей маски, проверку дескрипторов и переустановку маски сигнала.
Интервал:
Закладка: