Вход/Регистрация
UNIX: разработка сетевых приложений
вернуться

Стивенс Уильям Ричард

Шрифт:

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

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 всегда является двусторонним, и мы можем и читать, и записывать на любом конце этого канала.

Функция select на сокете и считывающем конце канала

23-30
Мы вызываем функцию
select
и на сокете, и на считывающем конце канала.

47-52
Когда доставляется сигнал
SIGALRM
, наш обработчик сигналов записывает в канал 1 байт, в результате чего считывающий конец канала становится готовым для чтения. Наш обработчик сигнала также возвращает управление, возможно, прерывая функцию
select
. Следовательно, если функция
select
возвращает ошибку
EINTR
, мы игнорируем эту ошибку, зная, что считывающий конец канала также готов для чтения, что завершит цикл
for
.

Чтение из канала

38-41
Когда считывающий конец канала готов для чтения, мы с помощью функции read считываем нулевой байт, записанный обработчиком сигнала, и игнорируем его. Но прибытие этого нулевого байта указывает нам на то, что истекло время таймера, и мы с помощью функции
break
выходим из бесконечного цикла
for
.

20.6. Резюме

При широковещательной передаче посылается дейтаграмма, которую получают все узлы. Недостатком широковещательной передачи является то, что каждый узел в подсети должен обрабатывать дейтаграмму, вплоть до уровня UDP в случае дейтаграммы UDP, даже если на узле не выполняется приложение-адресат. Для приложений с большими потоками данных, таких как аудио- и видео-приложения, это может привести к повышенной нагрузке на все узлы. В следующей главе мы увидим, что многоадресная передача решает эту проблему, поскольку позволяет не получать дейтаграмму узлам, не заинтересованным в этом.

Использование версии нашего эхо-клиента UDP, который отправляет серверу времени и даты широковещательные дейтаграммы и затем выводит все его ответы, полученные в течение 5 с, позволяет нам рассмотреть ситуацию гонок, возникающую при применении сигнала

SIGALRM
. Общим способом помещения тайм-аута в операцию чтения является использование функции
alarm
и сигнала
SIGALRM
, но он несет в себе неявную ошибку, типичную для сетевых приложений. Мы показали один некорректный и три корректных способа решения этой проблемы:

  • Читать дальше
  • 1
  • ...
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: