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 — протокол синхронизации времени).
Фрагментация IP-пакетов и широковещательная передача
В Беркли-ядрах фрагментация широковещательных дейтаграмм запрещена. Если размер IP-дейтаграммы, посылаемой на широковещательный адрес, превышает размер MTU исходящего интерфейса, возвращается ошибка EMSGSIZE
[128, с. 233–234]. Эта стратегия впервые появилась в 4.2BSD. На самом деле нет никаких технических препятствий для фрагментирования широковещательных дейтаграмм, но широковещательная передача сама по себе связана со значительной нагрузкой на сеть, поэтому не стоит дополнительно увеличивать эту нагрузку, используя фрагментацию.