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

ЖАНРЫ

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

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 < 0) {

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;

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