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

ЖАНРЫ

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

7 {

8 int n;

9 const int on = 1;

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

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 Signal(SIGALRM, recvfrom_alarm);

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

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

18 alarm(5);

19 for (;;) {

20 if (sigsetjmp(jmpbuf, 1) != 0)

21 break;

22 len = servlen;

23 n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);

24 recvline[n] = 0; /* null terminate */

25 printf("from %s: %s",

26 Sock_ntop_host(preply_addr, len), recvline);

27 }

28 }

29 free(preply_addr);

30 }

31 static void

32 recvfrom_alarm(int signo)

33 {

34 siglongjmp(jmpbuf, 1);

35 }

Размещение
буфера перехода в памяти

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

Вызов функции sigsetjmp

20-23
Когда мы вызываем функцию
sigsetjmp
непосредственно из нашей функции
dg_cli
, она устанавливает буфер перехода и возвращает нуль. Мы продолжаем работать дальше и вызываем функцию
recvfrom
.

Обработка сигнала SIGALRM и вызов функции siglongjmp

31-35
Когда сигнал доставлен, мы вызываем функцию
siglongjmp
. Это заставляет
sigsetjmp
в функции
dg_cli
возвратить значение, равное второму аргументу (1), который должен быть ненулевым. Это приведет к завершению цикла
for
в функции
dg_cli
.

Использование функций

sigsetjmp
и
siglongjmp
подобным образом гарантирует, что мы не останемся навсегда блокированы в вызове функции
recvfrom
из-за доставки сигнала в неподходящее время. Однако такое решение создает иную потенциальную проблему. Если сигнал доставляется в тот момент, когда функция
printf
осуществляет вывод данных, управление будет передано из
printf
обратно на
sigsetjmp
. При этом в структурах данных
printf
могут возникнуть противоречия. Чтобы предотвратить эту проблему, следует объединить блокирование и разблокирование сигналов, показанное в листинге 20.2, с помощью нелокального оператора
goto
.

Применение IPC в обработчике сигнала функции

Существует еще один корректный путь решения нашей проблемы. Вместо того чтобы просто возвращать управление и, как

мы надеемся, прерывать блокированную функцию
recvfrom
, наш обработчик сигнала при помощи средств IPC (Interprocess Communications — взаимодействие процессов) может сообщить функции
dg_cli
о том, что время таймера истекло. Это аналогично предложению, сделанному нами раньше, когда обработчик сигнала устанавливал глобальную переменную
had_alarm
по истечении времени таймера. Глобальная переменная использовалась как некая разновидность IPC (поскольку она была доступна и нашей функции, и обработчику сигнала). Однако при таком решении наша функция должна была проверять эту переменную, что могло привести к проблемам синхронизации в том случае, когда сигнал доставлялся приблизительно в это же время.

Листинг 20.6 демонстрирует использование канала внутри процесса. Обработчик сигналов записывает в канал 1 байт, когда истекает время таймера, а наша функция

dg_cli
считывает этот байт, чтобы определить, когда завершить свой цикл
for
. Что замечательно в этом решении — проверка готовности канала осуществляется функцией
select
. С ее помощью мы проверяем, готов ли к считыванию сокет или канал.

Листинг 20.6. Использование канала в качестве IPC между обработчиком сигнала и нашей функцией

//bcast/dgclibcast6.c

1 #include "unp.h"

2 static void recvfrom_alarm(int);

3 static int pipefd[2];

4 void

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

6 {

7 int n, maxfdp1;

8 const int on = 1;

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

10 fd_set rset;

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 Pipe(pipefd);

16 maxfdp1 = max(sockfd, pipefd[0]) + 1;

17 FD_ZERO(&rset);

18 Signal(SIGALRM, recvfrom_alarm);

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

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

21 alarm(5);

22 for (;;) {

23 FD_SET(sockfd, &rset);

24 FD_SET(pipefd[0], &rset);

25 if ((n = select(maxfdp1, &rset, NULL, NULL, NULL)) < 0) {

26 if (errno == EINTR)

27 continue;

28 else

29 err_sys("select error");

30 }

31 if (FD_ISSET(sockfd, &rset)) {

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