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

Тут можно читать онлайн Уильям Стивенс - UNIX: разработка сетевых приложений - бесплатно ознакомительный отрывок. Жанр: comp-osnet, издательство Питер, год 2007. Здесь Вы можете читать ознакомительный отрывок из книги онлайн без регистрации и SMS на сайте лучшей интернет библиотеки ЛибКинг или прочесть краткое содержание (суть), предисловие и аннотацию. Так же сможете купить и скачать торрент в электронном формате fb2, найти и слушать аудиокнигу на русском языке или узнать сколько частей в серии и всего страниц в публикации. Читателям доступно смотреть обложку, картинки, описание и отзывы (комментарии) о произведении.
  • Название:
    UNIX: разработка сетевых приложений
  • Автор:
  • Жанр:
  • Издательство:
    Питер
  • Год:
    2007
  • Город:
    Санкт-Петербург
  • ISBN:
    5-94723-991-4
  • Рейтинг:
    4.33/5. Голосов: 91
  • Избранное:
    Добавить в избранное
  • Отзывы:
  • Ваша оценка:
    • 80
    • 1
    • 2
    • 3
    • 4
    • 5

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

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

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

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

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

Интервал:

Закладка:

Сделать

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

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

//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))

26 if (errno == EINTR)

27 continue;

28 else

29 err_sys("select error");

30 }

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

32 len = servlen;

33 n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr,

34 &len);

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

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

37 Sock_ntop_host(preply_addr, len), recvline);

38 }

39 if (FD_ISSET(pipefd[0], &rset)) {

40 Read(pipefd[0], &n, 1); /* истекшее время */

41 break;

42 }

43 }

44 }

45 free(preply_addr);

46 }

47 static void

48 recvfrom_alarm(int signo)

49 {

50 Write(pipefd[1], "", 1); /* в канал пишется один нулевой байт */

51 return;

52 }

Создание канала

15 Мы создаем обычный канал Unix. Возвращаются два дескриптора: pipefd[0]доступен для чтения, а pipefd[0] — для записи.

ПРИМЕЧАНИЕ

Мы могли бы использовать функцию socketpair и получить двусторонний канал. В некоторых системах, особенно SVR4, обычный канал Unix всегда является двусторонним, и мы можем и читать, и записывать на любом конце этого канала.

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать


Уильям Стивенс читать все книги автора по порядку

Уильям Стивенс - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки LibKing.




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


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


Понравилась книга? Поделитесь впечатлениями - оставьте Ваш отзыв или расскажите друзьям

Напишите свой комментарий
x