1 #include "unp.h"
2 #define SAP_NAME "sap.mcast.net" /* имя группы и порт по умолчанию */
3 #define SAP_PORT "9875"
4 void loop(int, socklen_t);
5 int
6 main(int argc, char **argv)
7 {
8 int sockfd;
9 const int on = 1;
10 socklen_t salen;
11 struct sockaddr *sa;
12 if (argc == 1)
13 sockfd = Udp_client(SAP_NAME, SAP_PORT, (void**)&sa, &salen);
14 else if (argc == 4)
15 sockfd = Udp_client(argv[1], argv[2], (void**)&sa, &salen);
16 else
17 err_quit("usage: mysdr
18 Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
19 Bind(sockfd, sa, salen);
20 Mcast_join(sockfd, sa, salen, (argc == 4) ? argv[3], NULL, 0);
21 loop(sockfd, salen); /* получение и вывод */
22 exit(0);
23 }
2-3
Адрес многоадресной передачи, заданный для анонсов SAP — 224.2.127.254, а его имя — sap.mcast.net
. Все заранее известные адреса многоадресной передачи (см. http://www.iana.org/assignments/multicast-addresses
) появляются в DNS в иерархии mcast.net
. Заранее известный порт UDP — это порт 9875.
12-17
Мы вызываем нашу функцию udp_client
, чтобы просмотреть имя и порт, и она заполняет соответствующую структуру адреса сокета. Если не заданы аргументы командной строки, мы используем значения по умолчанию. В противном случае мы получаем адрес многоадресной передачи, порт и имя интерфейса из аргументов командной строки.
18-19
Мы устанавливаем параметр сокета SO_REUSEADDR
, чтобы позволить множеству экземпляров этой программы запуститься на узле, и с помощью функции bind связываем порт с сокетом. Связывая адрес многоадресной передачи с сокетом, мы запрещаем сокету получать какие-либо другие дейтаграммы UDP, которые могут быть получены для этого порта. Связывание этого адреса многоадресной передачи не является обязательным, но оно обеспечивает возможность фильтрации, благодаря чему ядро может не принимать пакеты, которые его не интересуют.
20
Мы вызываем нашу функцию mcast_join
, чтобы присоединиться к группе. Если имя интерфейса было задано в качестве аргумента командной строки, оно передается нашей функции, иначе мы позволяем ядру выбрать интерфейс, на котором будет происходить присоединение к группе.
21
Мы вызываем нашу функцию loop
, показанную в листинге 21.6, чтобы прочитать и вывести все анонсы.
Листинг 21.6. Цикл, получающий и выводящий анонсы SAP/SDP
//mysdr/loop.c
1 #include "mysdr.h"
2 void
3 loop(int sockfd, socklen_t salen)
4 {
5 socklen_t len;
6 ssize_t n;
7 char *p;
8 struct sockaddr *sa;
9 struct sap_packet {
10 uint32_t sap_header;
11 uint32_t sap_src;
12 char sap_data[BUFFSIZE];
13 } buf;
14 sa = Malloc(salen);
15 for (;;) {
15 len = salen;
17 n = Recvfrom(sockfd, &buf, sizeof(buf) - 1, 0, sa, &len);
18 ((char *)&buf)[n] = 0; /* завершающий нуль */
19 buf.sap_header = ntohl(buf.sap_header);
20 printf("From %s hash 0х%0х\n" Sock_ntop(sa, len),
21 buf.sap_header & SAP_HASH_MASK);
22 if (((buf.sap_header & SAP_VERSION_MASK) >> SAP_VERSION_SHIFT) > 1) {
23 err_msg("... version field not 1 (0x%08x)", buf.sap_header);
24 continue;
25 }
26 if (buf.sap_header & SAP_IPV6) {
27 err_msg("... IPv6");
28 continue;
29 }
30 if (buf.sap_header & (SAP_DELETE|SAP_ENCRYPTED|SAP_COMPRESSED)) {