KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программное обеспечение » Уильям Стивенс - UNIX: разработка сетевых приложений

Уильям Стивенс - UNIX: разработка сетевых приложений

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Уильям Стивенс, "UNIX: разработка сетевых приложений" бесплатно, без регистрации.
Перейти на страницу:

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 Выполняется присоединение к группе.

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

В листинге 21.4 показана наша функция mcast_set_loop.

Поскольку аргументом является дескриптор сокета, а не структура адреса сокета, мы вызываем нашу функцию sockfd_to_family, чтобы получить семейство адресов сокета. Устанавливается соответствующий параметр сокета.

Мы не показываем исходный код для всех остальных функций mcast_XXX, так как он свободно доступен в Интернете (см. предисловие).

Листинг 21.4. Установка параметра закольцовки для многоадресной передачи

//lib/mcast_set_loop.c

 1 #include "unp.h"


 2 int

 3 mcast_set_loop(int sockfd, int onoff)

 4 {

 5  switch (sockfd_to_family(sockfd)) {

 6  case AF_INET:{

 7   u_char flag;


 8   flag = onoff;

 9   return (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP,

10    &flag, sizeof(flag)));

11  }


12 #ifdef IPV6

13  case AF_INET6:{

14   u_int flag;


15   flag = onoff;

16   return (setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,

17    &flag, sizeof(flag)));

18  }

19 #endif


20  default:

21   errno = EPROTONOSUPPORT;

22   return (-1);

23  }

24 }

21.8 Функция dg_cli, использующая многоадресную передачу

Мы изменяем нашу функцию dg_cli, показанную в листинге 20.1, просто удаляя вызов функции setsockopt. Как мы сказали ранее, для отправки дейтаграмм многоадресной передачи не нужно устанавливать ни одного параметра сокета многоадресной передачи, если нас устраивают заданные по умолчанию настройки интерфейса исходящих пакетов, значения TTL и параметра закольцовки. Мы запускаем нашу программу, задавая в качестве адреса получателя группу всех узлов (all-hosts group):

macosx % udpcli01 224.0.1.1

hi there

from 172.24.37.78: hi there MacOS X

from 172.24.37.94: hi there FreeBSD

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

Фрагментация IP и многоадресная передача

В конце раздела 20.4 мы отмечали, что в большинстве систем фрагментация широковещательной дейтаграммы не допускается по стратегическим соображениям. Фрагментация допускается при многоадресной передаче, что мы можем легко проверить, используя тот же файл с 2000-байтовой строкой:

macosx % udpcli01 224.0.1.1 < 2000line

from 172.24.37.78: xxxxxxx[...]

from 172.24.37.94: xxxxxxx[...]

21.9. Получение анонсов сеансов многоадресной передачи

Многоадресная инфраструктура представляет собой часть Интернета, в которой разрешена многоадресная передача между доменами. Во всем Интернете многоадресная передача не разрешена. Многоадресная инфраструктура Интернета начала свое существование в 1992 году. Тогда она называлась MBone и была оверлейной сетью. В 1998 году MBone была признана частью инфраструктуры Интернета. Внутри предприятий многоадресная передача используется достаточно широко, но междоменная передача поддерживается гораздо меньшим числом серверов.

Для участия в мультимедиа-конференции по сети многоадресной передачи достаточно того, чтобы сайту был известен групповой адрес конференции и порты UDP для потоков данных (например, аудио и видео). Протокол анонсирования сеансов (Session Announcement Protocol, SAP) определяет эту процедуру, описывая заголовки пакетов и частоту, с которой эти анонсы при помощи многоадресной передачи передаются по инфраструктуре многоадресной передачи. Этот протокол описан в RFC 2974 [42]. Протокол описания сеанса (Session Description Protocol, SDP) [41] описывает технические параметры сеанса связи (в частности, он определяет, как задаются адреса многоадресной передачи и номера портов UDP). Сайт, желающий анонсировать сеанс, периодически посылает пакет многоадресной передачи, содержащий описание сеанса, для известной группы на известный порт UDP. Для получения этих анонсов сайты запускают программу под названием sdr. Эта программа не только получает объявления сеансов, но и предоставляет интерактивный интерфейс пользователя, позволяющий пользователю отправлять свои собственные анонсы.

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

В листинге 21.5 показана наша программа main, получающая периодические анонсы SAP/SDP.

Листинг 21.5. Программа main, получающая периодические анонсы SAP/SDP

//mysdr/main.c

 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);

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*