22.6. Связывание с адресами интерфейсов

22.6. Связывание с адресами интерфейсов

Одно из типичных применений функции get_ifi_info связано с приложениями UDP, которым нужно выполнять мониторинг всех интерфейсов на узле, чтобы знать, когда и на какой интерфейс приходит дейтаграмма. Это позволяет получающей программе узнавать адрес получателя дейтаграммы UDP, так как именно по этому адресу определяется сокет, на который доставляется дейтаграмма, даже если узел не поддерживает параметр сокета IP_RECVDSTADDR.

ПРИМЕЧАНИЕ

Вспомните наше обсуждение в конце раздела 22.2. Если узел использует более распространенную модель системы с гибкой привязкой (см. раздел 8.8), IP-адрес получателя может отличаться от IP-адреса принимающего интерфейса. В этом случае мы можем определить только адрес получателя дейтаграммы, который не обязательно должен быть адресом, присвоенным принимающему интерфейсу. Чтобы определить принимающий интерфейс, требуется параметр сокета IP_RECVIF или IPV6_PKTINFO.

В листинге 22.13 показана первая часть примера применения этой технологии к эхо-серверу UDP, который связывается со всеми адресами направленной передачи, широковещательной передачи и, наконец, с универсальными адресами.

Листинг 22.13. Первая часть сервера UDP, который с помощью функции bind связывается со всеми адресами

//advio/udpserv03.c

 1 #include "unpifi.h"

 2 void mydg_echo(int, SA*, socklen_t, SA*);

 3 int

 4 main(int argc, char **argv)

 5 {

 6  int sockfd;

 7  const int on = 1;

 8  pid_t pid;

 9  struct ifi_info *ifi, *ifihead;

10  struct sockaddr_in *sa, cliaddr, wildaddr;

11  for (ifihead = ifi = Get_ifi_info(AF_INET, 1);

12   ifi != NULL; ifi = ifi->ifi_next) {

13   /* связываем направленный адрес */

14   sockfd = Socket(AF_INET, SOCK_DGRAM, 0);

15   Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

16   sa = (struct sockaddr_in*)ifi->ifi_addr;

17   sa->sin_family = AF_INET;

18   sa->sin_port = htons(SERV_PORT);

19   Bind(sockfd, (SA*)sa, sizeof(*sa));

20   printf("bound %s ", Sock_ntop((SA*)sa, sizeof(*sa)));

21   if ((pid = Fork()) == 0) { /* дочерний процесс */

22    mydg_echo(sockfd, (SA*)&cliaddr, sizeof(cliaddr), (SA*)sa);

23    exit(0); /* не выполняется */

24   }

Вызов функции get_ifi_info для получения информации об интерфейсе

11-12 Функция get_ifi_info получает все адреса IPv4, включая дополнительные (псевдонимы), для всех интерфейсов. Затем программа перебирает все структуры ifi_info.

Создание сокета UDP и связывание адреса направленной передачи

13-20 Создается сокет UDP, и с ним связывается адрес направленной передачи. Мы также устанавливаем параметр сокета SO_REUSEADDR, поскольку мы связываем один и тот же порт (параметр SERV_PORT) для всех IP-адресов.

ПРИМЕЧАНИЕ

Не все реализации требуют, чтобы был установлен этот параметр сокета. Например, Беркли-реализации не требуют этого параметра и позволяют с помощью функции bind связать уже связанный порт, если новый связываемый IP-адрес не является универсальным адресом и отличается от всех IP-адресов, уже связанных с портом. Однако Solaris 2.5 для успешного связывания с одним и тем же портом второго адреса направленной передачи требует установки этого параметра.

Порождение дочернего процесса для данного адреса

21-24 Вызывается функция fork, порождающая дочерний процесс. В этом дочернем процессе вызывается функция mydg_echo, которая ждет прибытия любой дейтаграммы на сокет и отсылает ее обратно отправителю.

В листинге 22.14 показана следующая часть функции main, которая обрабатывает широковещательные адреса.

Листинг 22.14. Вторая часть сервера UDP, который с помощью функции bind связывается со всеми адресами

//advio/udpserv03.c

25   if (ifi->ifi_flags & IFF_BROADCAST) {

26    /* пытаемся связать широковещательный адрес */

27    sockfd = Socket(AF_INET, SOCK_DGRAM, 0);

28    Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

29    sa = (struct sockaddr_in*)ifi->ifi_brdaddr;

30    sa->sin_family = AF_INET;

31    sa->sin_port = htons(SERV_PORT);

32    if (bind(sockfd, (SA*)sa, sizeof(*sa)) < 0) {

33     if (errno == EADDRINUSE) {

34      printf("EADDRINUSE: %s ",

35       Sock_ntop((SA*)sa, sizeof(*sa)));

36      Close(sockfd);

37      continue;

38     } else

39      err_sys("bind error for %s",

40       Sock_ntop((SA*)sa, sizeof(*sa)));

41    }

42    printf("bound %s ", Sock_ntop((SA*)sa, sizeof(*sa)));

43    if ((pid = Fork()) == 0) { /* дочерний процесс */

44     mydg_echo(sockfd, (SA*)&cliaddr, sizeof(cliaddr),

45      (SA*)sa);

46     exit(0); /* не выполняется */

47    }

48   }

49  }

Связывание с широковещательными адресами

25-42 Если интерфейс поддерживает широковещательную передачу, создается сокет UDP и с ним связывается широковещательный адрес. На этот раз мы позволим функции bind завершиться с ошибкой EADDRINUSE, поскольку если у интерфейса имеется несколько дополнительных адресов (псевдонимов) в одной подсети, то каждый из различных адресов направленной передачи будет иметь один и тот же широковещательный адрес. Подобный пример приведен после листинга 17.3. В этом сценарии мы предполагаем, что успешно выполнится только первая функция bind.

Порождение дочернего процесса

43-47 Порождается дочерний процесс, и он вызывает функцию mydg_echo.

Заключительная часть функции main показана в листинге 22.15. В этом коде при помощи функции bind происходит связывание с универсальным адресом для обработки любого адреса получателя, отличного от адресов направленной и широковещательной передачи, которые уже связаны. На этот сокет будут приходить только дейтаграммы, предназначенные для ограниченного широковещательного адреса (255.255.255.255).

Листинг 22.15. Заключительная часть сервера UDP, связывающегося со всеми адресами

//advio/udpserv03.c

50  /* связываем универсальный адрес */

51  sockfd = Socket(AF_INET, SOCK_DGRAM, 0);

52  Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

53  bzero(&wildaddr, sizeof(wildaddr));

54  wildaddr.sin_family = AF_INET;

55  wildaddr.sin_addr.s_addr = htonl(INADDR_ANY);

56  wildaddr.sin_port = htons(SERV_PORT);

57  Bind(sockfd, (SA*)&wildaddr, sizeof(wildaddr));

58  printf("bound %s ", Sock_ntop((SA*)&wildaddr, sizeof(wildaddr)));

59  if ((pid = Fork()) == 0) { /* дочерний процесс */

60   mydg_echo(sockfd, (SA*)&cliaddr, sizeof(cliaddr), (SA*)sa);

61   exit(0); /* не выполняется */

62  }

63  exit(0);

64 }

Создание сокета и связывание с универсальным адресом

50-62 Создается сокет UDP, устанавливается параметр сокета SO_REUSEADDR и происходит связывание с универсальным IP-адресом. Порождается дочерний процесс, вызывающий функцию mydg_echo.

Завершение работы функции main

63 Функция main завершается, и сервер продолжает выполнять работу, как и все порожденные дочерние процессы.

Функция mydg_echo, которая выполняется всеми дочерними процессами, показана в листинге 22.16.

Листинг 22.16. Функция mydg_echo

//advio/udpserv03.c

65 void

66 mydg_echo(int sockfd, SA *pcliaddr, socklen_t clilen, SA *myaddr)

67 {

68  int n;

69  char mesg[MAXLINE];

70  socklen_t len;

71  for (;;) {

72   len = clilen;

73   n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);

74   printf("child %d, datagram from %s", getpid(),

75   Sock_ntop(pcliaddr, len));

76   printf(", to %s ", Sock_ntop(myaddr, clilen));

77   Sendto(sockfd, mesg, n, 0, pcliaddr, len);

78  }

79 }

Новый аргумент

65-66 Четвертым аргументом этой функции является IP-адрес, связанный с сокетом. Этот сокет должен получать только дейтаграммы, предназначенные для данного IP-адреса. Если IP-адрес является универсальным, сокет должен получать только те дейтаграммы, которые не подходят ни для какого другого сокета, связанного с тем же портом.

Чтение дейтаграммы и отражение ответа

71-78 Дейтаграмма читается с помощью функции recvfrom и отправляется клиенту обратно с помощью функции sendto. Эта функция также выводит IP-адрес клиента и IP-адрес, который был связан с сокетом.

Запустим эту программу на нашем узле solaris после установки псевдонима для интерфейса hme0 Ethernet. Адрес псевдонима: узел 200 в сети 10.0.0/24.

solaris % udpserv03

bound 127.0.0.1:9877     интерфейс закольцовки

bound 10.0.0.200:9877    направленный адрес интерфейса hme0:1

bound 10.0.0.255:9877    широковещательный адрес интерфейса hme0:1

bound 192.168.1.20:9877  направленный адрес интерфейса hme0

bound 192.168.1.255:9877 широковещательный адрес интерфейса hme0

bound 0.0.0.0.9877       универсальный адрес

При помощи утилиты netstat мы можем проверить, что все сокеты связаны с указанными IP-адресами и портом:

solaris % netstat -na | grep 9877

127.0.0.1.9877       Idle

10.0.0.200.9877      Idle

    *.9877           Idle

192.129.100.100.9877 Idle

    *.9877           Idle

    *.9877           Idle

Следует отметить, что для простоты мы создаем по одному дочернему процессу на сокет, хотя возможны другие варианты. Например, чтобы ограничить число процессов, программа может управлять всеми дескрипторами сама, используя функцию select и не вызывая функцию fork. Проблема в данном случае будет заключаться в усложнении кода. Хотя использовать функцию select для всех дескрипторов несложно, нам придется осуществить некоторое сопоставление каждого дескриптора связанному с ним IP-адресу (вероятно, с помощью массива структур), чтобы иметь возможность вывести IP-адрес получателя после того, как на определенном сокете получена дейтаграмма. Часто бывает проще использовать отдельный процесс или поток для каждой операции или дескриптора вместо мультиплексирования множества различных операций или дескрипторов одним процессом.

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

R.3.3 Программа и связывание

Из книги Справочное руководство по C++ автора Страустрап Бьярн

R.3.3 Программа и связывание Программа состоит из одного или нескольких файлов, связываемых вместе (§R.2). Файл состоит из последовательности описаний. Имя с файловой областью видимости, которое явно описано как static, является локальным в своей единице трансляции и может


Динамическое связывание

Из книги Язык программирования С# 2005 и платформа .NET 2.0. [3-е издание] автора Троелсен Эндрю

Динамическое связывание Упрощенно говоря, динамическое связывание, или динамическая привязка, - это подход, с помощью которого можно создавать экземпляры заданного типа и вызывать их члены в среде выполнения и условиях, когда во время компиляции о типе еще ничего не


Связывание видов

Из книги ArchiCAD 11 автора Днепров Александр Г

Связывание видов Среди инструментов визуализации ArchiCAD существует механизм, назначение которого – одновременное совместное отображение двух различных видов. Какой в этом смысл?Необходимость в этом возникает довольно часто. Например, для визуальной привязки объектов


Динамическое связывание

Из книги Основы объектно-ориентированного программирования автора Мейер Бертран

Динамическое связывание Сочетание последних двух механизмов, переопределения и полиморфизма, непосредственно предполагает следующий механизм. Допустим, есть вызов, целью которого является полиморфная сущность, например сущность типа BOAT вызывает компонент turn.


Связывание с АТД

Из книги Системное программирование в среде Windows автора Харт Джонсон М

Связывание с АТД Класс, как неоднократно говорилось, является реализацией АТД, заданного формальной спецификацией или неявно подразумеваемого. В начале лекции отмечалось, что утверждения можно рассматривать, как способ введения в класс семантических свойств, лежащих в


Динамическое связывание

Из книги TCP/IP Архитектура, протоколы, реализация (включая IP версии 6 и IP Security) автора Фейт Сидни М

Динамическое связывание Динамическое связывание дополнит переопределение, полиморфизм и статическую типизацию, создавая базисную тетралогию


Типизация и связывание

Из книги Разработка приложений в среде Linux. Второе издание автора Джонсон Майкл К.

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


Неявное связывание

Из книги Linux: Полное руководство автора Колисниченко Денис Николаевич

Неявное связывание Неявное связывание, или связывание во время загрузки (load-time linking) является простейшей из двух методик связывания. Порядок действий в случае использования Microsoft C++ следующий:1. После того как собраны все необходимые для новой DLL функции, осуществляется


Явное связывание

Из книги Операционная система UNIX автора Робачевский Андрей М.

Явное связывание Явное связывание, или связывание во время выполнения (run-time linking), требует, чтобы в программе содержались конкретные указания относительно того, когда именно необходимо загрузить или освободить библиотеку DLL. Далее программа получает адрес запрошенной


Связывание сокета

Из книги C++ для начинающих автора Липпман Стенли

Связывание сокета Следующий шаг заключается в привязке сокета к его адресу и конечной точке (endpoint) (направление канала связи от приложения к службе). Вызов socket, за которым следует вызов bind, аналогичен созданию именованного канала. Однако не существует имен, используя


11.9.3 Связывание

Из книги автора

11.9.3 Связывание Сервер DHCP хранит таблицу соответствия между клиентами и их конфигурационными параметрами. Связывание заключается в назначении каждому клиенту IP-адреса и набора конфигурационных


17.8.1. Манипулирование IPv4-адресами

Из книги автора

17.8.1. Манипулирование IPv4-адресами Функции inet_ntop() и inet_pton() являются относительно новыми и были введены для того, чтобы один набор функций мог обрабатывать и IPv4-, и IPv6-адреса. До их появления в программах использовались функции inet_addr(), inet_aton() и inet_ntoa(), которые предназначены


19.3.4. Операции над адресами: команда ip address

Из книги автора

19.3.4. Операции над адресами: команда ip address Команда ip address управляет адресами на устройстве. Объект для нее — это IPv4 или IPv6 адрес. Эта команда показывает адреса и их свойства, а также добавляет новые адреса.Чтобы добавить адрес 192.168.0.1/24 с маской подсети 255.255.255.0 со стандартным


Связывание (binding)

Из книги автора

Связывание (binding) Прежде чем клиент сможет вызвать удаленную процедуру, необходимо связать его с удаленной системой, располагающей требуемым сервером. Таким образом, задача связывания распадается на две:? Нахождение удаленного хоста с требуемым сервером? Нахождение


9.1.7. Безопасное связывание A

Из книги автора

9.1.7. Безопасное связывание A При использовании перегрузки складывается впечатление, что в программе можно иметь несколько одноименных функций с разными списками параметров. Однако это лексическое удобство существует только на уровне исходного текста. В большинстве