20.4. Функция dg_cli при использовании широковещательной передачи

20.4. Функция dg_cli при использовании широковещательной передачи

Мы еще раз изменим нашу функцию dg_cli, на этот раз дав ей возможность отправлять широковещательные сообщения стандартному серверу времени и даты UDP (см. табл. 2.1) и выводить все ответы. Единственное изменение, внесенное нами в функцию main (см. листинг 8.3), состоит в изменении номера порта получателя на 13:

servaddr.sin_port = htons(13);

Сначала мы откомпилируем измененную функцию main с прежней функцией dg_cli из листинга 8.4 и запустим ее на узле freebsd:

freebsd % udpcli01 192.168.42.255

hi

sendto error: Permission denied

Аргумент командной строки — это широковещательный адрес подсети для присоединенной сети Ethernet. Мы вводим строку, программа вызывает функцию sendto, и возвращается ошибка EACCESS. Мы получаем ошибку, потому что нам не разрешается посылать дейтаграмму на широковещательный адрес получателя, если мы не указали ядру явно, что будем передавать широковещательное сообщение. Мы выполняем это условие, установив параметр сокета SO_BROADCAST (см. табл. 7.1).

ПРИМЕЧАНИЕ

Беркли-реализации реализуют эту «защиту от дурака» (sanity check). Однако Solaris 2.5 принимает дейтаграмму, предназначенную для широковещательного адреса, даже если мы не задаем параметр сокета SO_BROADCAST. Стандарт POSIX требует установки параметра сокета SO_BROADCAST для отправки широковещательной дейтаграммы.

В 4.2BSD широковещательная передача была привилегированной операцией, и параметра сокета SO_BROADCAST не существовало. В 4.3BSD этот параметр был добавлен и каждому процессу стало разрешено его устанавливать.

Теперь мы изменим нашу функцию dg_cli, как показано в листинге 20.1[1]. Эта версия устанавливает параметр сокета SO_BROADCAST и выводит все ответы, полученные в течение 5 с.

Листинг 20.1. Функция dg_cli, осуществляющая широковещательную передачу

//bcast/dgclibcast1.c

 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  socklen_t len;

10  struct sockaddr *preply_addr;

11  preply_addr = Malloc(servlen);

12  Setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

13  Signal(SIGALRM, recvfrom_alarm);

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

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

16   alarm(5);

17   for (;;) {

18    len = servlen;

19    n = recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);

20    if (n < 0) {

21     if (errno == EINTR)

22      break; /* окончание ожидания ответов */

23     else

24      err_sys("recvfrom error");

25    } else {

26     recvline[n] = 0; /* завершающий нуль */

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

28      Sock_ntop_host(preply_addr, len), recvline);

29    }

30   }

31  }

32  free(preply_addr);

33 }

34 static void

35 recvfrom_alarm(int signo)

36 {

37  return; /* прерывание recvfrom() */

38 }

Выделение памяти для адреса сервера, установка параметра сокета

11-13 Функция malloc выделяет в памяти пространство для адреса сервера, возвращаемого функцией recvfrom. Устанавливается параметр сокета SO_BROADCAST, устанавливается обработчик сигнала SIGALRM.

Чтение строки, отправка сокету, чтение всех ответов

14-24 Следующие два вызова, fgets и sendto, выполняются так же, как и в предыдущих версиях этой функции. Но поскольку мы посылаем широковещательную дейтаграмму, мы можем получить множество ответов. Мы вызываем в цикле функцию recvfrom и выводим все ответы, полученные в течение 5 с. По истечении 5 с генерируется сигнал SIGALRM, вызывается наш обработчик сигнала и функция recvfrom возвращает ошибку EINTR.

Вывод каждого полученного ответа

25-29 Для каждого полученного ответа мы вызываем функцию sock_ntop_host, которая в случае IPv4 возвращает строку, содержащую IP-адрес сервера в точечно-десятичной записи. Эта строка выводится вместе с ответом сервера.

Если мы запустим программу, задав широковещательный адрес подсети 192. 168.42.255, мы увидим следующее:

bsdi % udpcli01 192.168.42.255 hi

from 192 168.42 2: Sat Aug 2 16.42.45 2003

from 192.168.42.1: Sat Aug 2 14.42.45 2003

from 192.168.42.3: Sat Aug 2 14.42.45 2003

hello

from 192.168.42.3: Sat Aug 2 14.42.57 2003

from 192.168.42.2: Sat Aug 2 16.42.57 2003

from 192.168.42.1: Sat Aug 2 14.42.57 2003

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

Все системы сообщают одно и то же время, поскольку на них используется NTP (Network Time Protocol — протокол синхронизации времени).

Данный текст является ознакомительным фрагментом.