Уильям Стивенс - UNIX: разработка сетевых приложений Страница 153

Тут можно читать бесплатно Уильям Стивенс - UNIX: разработка сетевых приложений. Жанр: Компьютеры и Интернет / Программное обеспечение, год -. Так же Вы можете читать полную версию (весь текст) онлайн без регистрации и SMS на сайте 500book.ru или прочесть краткое содержание, предисловие (аннотацию), описание и ознакомиться с отзывами (комментариями) о произведении.
Уильям Стивенс - UNIX: разработка сетевых приложений
  • Категория: Компьютеры и Интернет / Программное обеспечение
  • Автор: Уильям Стивенс
  • Год выпуска: -
  • ISBN: -
  • Издательство: -
  • Страниц: 263
  • Добавлено: 2019-07-03 10:23:38
  • Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних просмотр данного контента СТРОГО ЗАПРЕЩЕН! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту pbn.book@yandex.ru для удаления материала


Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание

Прочтите описание перед тем, как прочитать онлайн книгу «Уильям Стивенс - UNIX: разработка сетевых приложений» бесплатно полную версию:
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.

Уильям Стивенс - UNIX: разработка сетевых приложений читать онлайн бесплатно

Уильям Стивенс - UNIX: разработка сетевых приложений - читать книгу онлайн бесплатно, автор Уильям Стивенс

Листинг 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 < 0) {

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 представлена простая, хотя и некорректная ее реализация. Мы приводим ее здесь, несмотря на некорректность, чтобы продемонстрировать три стадии решения: установку маски сигнала в значение, заданное вызывающей функцией, с сохранением текущей маски, проверку дескрипторов и переустановку маски сигнала.

Листинг 20.4. Простая некорректная реализация функции pselect

//lib/pselect.c

 9 #include "unp.h"

10 int

11 pselect(int nfds, fd_set *rset, fd_set *wset, fd_set *xset,

12  const struct timespec *ts, const sigset_t *sigmask)

13  {

14  int n;

15  struct timeval tv;

16  sigset_t savemask;

17  if (ts != NULL) {

18   tv.tv_sec = ts->tv_sec;

19   tv.tv_usec = ts->tv_nsec / 1000; /* наносекунды -> микросекунды */

20  }

21  sigprocmask(SIG_SETMASK, sigmask, &savemask); /* маска вызывающего

                                                     процесса */

22  n = select(nfds, rset, wset, xset., (ts == NULL) ? NULL : &tv);

23  sigprocmask(SIG_SETMASK, &savemask, NULL); /* восстанавливаем

                                                  исходную маску */

24  return (n);

25 }

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

Нашу проблему можно решить корректно, если отказаться от прерывания блокированного системного вызова обработчиком сигнала, вместо этого вызвав из обработчика сигнала функцию siglongjmp. Этот метод называется нелокальным оператором goto (nonlocal goto), поскольку мы можем использовать его для перехода из одной функции в другую. В листинге 20.5 проиллюстрирована эта технология.

Листинг 20.5. Вызов функций sigsetjmp и siglongjmp из обработчика сигнала

//bcast/dgclibcast5.c

 1 #include "unp.h"

 2 #include <setjmp.h>

 3 static void recvfrom_alarm(int);

 4 static sigjmp_buf jmpbuf;

 5 void

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

 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 между обработчиком сигнала и нашей функцией

Перейти на страницу:
Вы автор?
Жалоба
Все книги на сайте размещаются его пользователями. Приносим свои глубочайшие извинения, если Ваша книга была опубликована без Вашего на то согласия.
Напишите нам, и мы в срочном порядке примем меры.
Комментарии / Отзывы
    Ничего не найдено.