22.4. Обычным решением будет создать по одному сокету на каждый адрес интерфейса, как было сделано в разделе 22.6, и отправлять ответ с того же сокета, на который пришел запрос.
22.5. Вызов функции
getaddrinfо
без аргумента имени узла и без флага
AI_PASSIVE
заставляет эту функцию считать, что используется локальный адрес 0::1 (для IPv6) или 127.0.0.1 (для IPv4). Напомним, что структура адреса сокета IPv6 возвращается функцией
getaddrinfo
перед структурой адреса сокета IPv4 при условии, что поддерживается протокол IPv6. Если узел поддерживает оба протокола, вызов функции socket в
udp_client
закончится успешно при указании семейства протоколов
AF_INET6
.
В листинге Д.9 приведена не зависящая от протокола версия программы.
Листинг Д.9. Не зависящая от протокола версия программы из раздела 22.6
//advio/udpserv04.c
1 #include "unpifi.h"
2 void mydg_echo(int, SA*, socklen_t);
3 int
4 main(int argc, char **argv)
5 {
6 int sockfd, family, port;
7 const int on = 1;
8 pid_t pid;
9 socklen_t salen;
10 struct sockaddr *sa, *wild;
11 struct ifi_info *ifi, *ifihead;
12 if (argc == 2)
13 sockfd = Udp_client(NULL, argv[1], (void**)sa, salen);
14 else if (argc == 3)
15 sockfd = Udp_client(argv[1], argv[2], (void**)sa, salen);
16 else
17 err_quit("usage; udpserv04 [ host ] service or port");
18 family = sa-sa_family;
19 port = sock_get_port(sa, salen);
20 Close(sockfd); /* хотим узнать семейство, порт salen */
21 for (ifihead = ifi = Get_ifi_info(family, 1),
22 ifi ! = NULL; ifi = ifi-ifi_next) {
23 /* связывание с многоадресными адресами */
24 sockfd = Socket(family, SOCK_DGRAM, 0);
25 Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on));
26 sock_set_port(ifi-ifi_addr, salen, port);
27 Bind(sockfd, ifi-ifi_addr, salen);
28 printf("bound %s\n", Sock_ntop(ifi-ifi_addr, salen));
29 if ((pid = Fork) == 0) { /* дочерний процесс */
30 mydg_echo(sockfd, ifi-ifi_addr, salen);
31 exit(0); /* никогда не выполняется */
32 }
33 if (ifi-ifi_flags IFF_BROADCAST) {
34 /* попытка связывания с широковещательным адресом */
35 sockfd = Socket(family, SOCK_DGRAM, 0);
36 Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on));
37 sock_set_port(ifi-ifi_brdaddr, salen, port);
38 if (bind(sockfd, ifi-ifi_brdaddr, salen) 0) {
39 if (errno == EADDRINUSE) {
40 printf("EADDRINUSE: %s\n",
41 Sock_ntop(ifi-ifi_brdaddr, salen));
42 Close(sockfd);
43 continue;
44 } else
45 err_sys("bind error for %s",
46 Sock_ntop(ifi-ifi_brdaddr, salen));
47 }
48 printf ("bound %s\n", Sock_ntop(ifi-ifi_brdaddr, salen));
49 if ((pid = Fork) == 0) { /* дочерний процесс */
50 mydg_echo(sockfd, ifi-ifi_brdaddr, salen);
51 exit(0); /* никогда не выполняется */
52 }
53 }
54 }
55 /* связывание с универсальным адресом */
56 sockfd = Socket(family, SOCK_DGRAM, 0);