Книга: UNIX: разработка сетевых приложений

Пример: функция mcast_join

Пример: функция mcast_join

В листинге 21.1[1] показана первая часть функции mcast_join. Эта часть демонстрирует простоту интерфейса программирования, не зависящего от протокола.

Листинг 21.1. Присоединение к группе: сокет IPv4

//lib/mcast_join.c
 1 #include "unp.h"
 2 #include <net/if.h>
 3 int
 4 mcast_join(int sockfd, const SA *grp, socklen_t grplen,
 5 const char *ifname, u_int ifindex)
 6 {
 7 #ifdef MCAST_JOIN_GROUP
 8  struct group_req req;
 9  if (ifindex > 0) {
10   req.gr_interface = ifindex;
11  } else if (ifname != NULL) {
12   if ((req.gr_interface = if_nametoindex(ifname)) == 0) {
13    errno = ENXIO; /* интерфейс не найден */
14    return(-1);
15   }
16  } else
17  req.gr_interface = 0;
18  if (grplen > sizeof(req.gr_group)) {
19   errno = EINVAL;
20   return -1;
21  }
22  memcpy(&req.gr_group, grp, grplen);
23  return (setsockopt(sockfd, family_to_level(grp->sa_family),
24  MCAST_JOIN_GROUP, &req, sizeof(req)));
25 #else

Обработка индекса

9-17 Если при вызове был указан индекс интерфейса, функция использует его непосредственно. В противном случае (при указании имени интерфейса), имя преобразуется в индекс вызовом if_nametoindex. Если ни имя, ни индекс не заданы, интерфейс выбирается ядром.

Копирование адреса и вызов setsockopt

18-22 Адрес сокета копируется непосредственно в поле группы. Вспомните, что поле это имеет тип sockaddr_storage, а потому достаточно велико для хранения адреса любого типа, поддерживаемого системой. Для предотвращения переполнения буфера (при ошибках в программе) мы проверяем размер sockaddr и возвращаем EINVAL, если он слишком велик.

23-24 Присоединение к группе выполняется вызовом setsockopt. Аргумент level определяется на основании семейства группового адреса вызовом нашей собственной функции family_to_level. Некоторые системы допускают несоответствие аргумента level семейству адреса сокета, например использование IPPROTO_IP с MCAST_JOIN_GROUP, даже если сокет относится к семейству AF_INET6, но это верно не для всех систем, поэтому мы и должны выполнить преобразование семейства к нужному значению level. Листинг этой тривиальной функции в книге мы не приводим, но исходный код этой функции вы можете скачать вместе со всеми остальными программами.

В листинге 21.2 представлена вторая часть функции mcast_join, обрабатывающая сокеты IPv4.

Листинг 21.2. Присоединение к группе: обработка сокета IPv4

26  switch (grp->sa_family) {
27  case AF_INET: {
28   struct ip_mreq mreq;
29   struct ifreq ifreq;
30   memcpy(&mreq.imr_multiaddr,
31    &((const struct sockaddr_in*)grp)->sin_addr,
32    sizeof(struct in_addr));
33    if (ifindex > 0) {
34     if (if_indextoname(ifindex, ifreq.ifr_name) == NULL) {
35      errno = ENXIO; /* i/f index not found */
36      return(-1);
37     }
38     goto doioctl;
39    } else if (ifname != NULL) {
40     strncpy(ifreq.ifr_name, ifname, IFNAMSIZ);
41 doioctl:
42     if (ioctl(sockfd, SIOCGIFADDR, &ifreq) < 0)
43      return(-1);
44     memcpy(&mreq.imr_interface,
45      &((struct sockaddr_in*)&ifreq.ifr_addr)->sin_addr,
46      sizeof(struct in_addr));
47    } else
48     mreq.imr_interface.s_addr = htonl(INADDR_ANY);
49    return(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
50     &mreq, sizeof(mreq)));
51   }

Обработка индекса

33-38 Адрес многоадресной передачи IPv4 в структуре адреса сокета копируется в структуру ip_mreq. Если индекс был задан, вызывается функция if_indextoname, сохраняющая имя в нашей структуре ip_mreq. Если это выполняется успешно, мы переходим на точку вызова ioctl.

Обработка имени

39-46 Имя вызывающего процесса копируется в структуру ip_mreq, а вызов SIOCGIFADDR функции ioctl возвращает адрес многоадресной передачи, связанный с этим именем. При успешном выполнении адрес IPv4 копируется в элемент imr_interface структуры ip_mreq.

Значения по умолчанию

47-48 Если ни индекс, ни имя не заданы, используется универсальный адрес, что указывает ядру на необходимость выбрать интерфейс.

49-50 Функция setsockopt выполняет присоединение к группе.

Третья, и последняя, часть функции, обрабатывающая сокеты IPv6, приведена в листинге 21.3.

Листинг 21.3. Присоединение к группе: обработка сокета IPv6

52 #ifdef IPV6
53  case AF_INET6: {
54   struct ipv6_mreq mreq6;
55   memcpy(&mreq6.ipv6mr_multiaddr,
56    &((const struct sockaddr_in6*) grp)->sin6_addr,
57    sizeof(struct in6_addr));
58    if (ifindex > 0) {
59     mreq6.ipv6mr_interface = ifindex;
60    } else if (ifname != NULL) {
61     if ((mreq6.ipv6mr_interface = if_nametoindex(ifname)) == 0) {
62      errno = ENXIO; /* интерфейс не найден */
63      return(-1);
64     }
65    } else
66     mreq6.ipv6mr_interface = 0;
67    return(setsockopt(sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
68     &mreq6, sizeof(mreq6)));
69   }
70 #endif
71  default:
72   errno = EAFNOSUPPORT;
73   return(-1);
74  }
75 #endif
76 }

Копирование адреса

55-57 Сначала адрес IPv6 копируется из структуры адреса сокета в структуру ipv6_mreq.

Обработка индекса или имени интерфейса или выбор интерфейса по умолчанию

58-66 Если был задан индекс, он записывается в элемент ipv6mr_interface. Если индекс не задан, но задано имя, то для получения индекса вызывается функция if_nametoindex. В противном случае для функции setsockopt индекс устанавливается в 0, что указывает ядру на необходимость выбрать интерфейс.

67-68 Выполняется присоединение к группе.

Оглавление книги


Генерация: 1.121. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз