Чтобы получить дейтаграмму многоадресной передачи, процесс должен присоединиться к группе, а также связать при помощи функции
bind
сокет UDP с номером порта, который будет использоваться как номер порта получателя для дейтаграмм, отсылаемых данной группе. Это две отдельные операции, и обе они являются обязательными. Присоединение к группе указывает уровню IP узла и канальному уровню, что необходимо получать дейтаграммы многоадресной передачи, отправленные этой группе. Связывая порт, приложение указывает UDP, что требуется получать отправляемые на этот порт дейтаграммы. Некоторые приложения в дополнение к связыванию порта также связывают при помощи функции
bind
адрес многоадресной передачи с сокетом. Это предотвращает доставку сокету любых других дейтаграмм, которые могли быть получены для этого порта.
ПРИМЕЧАНИЕ
Исторически Беркли-реализации требуют только, чтобы некоторый сокет на узле присоединился к группе — это не обязательно тот сокет, который связывается с портом и затем получает дейтаграммы многоадресной передачи. Однако есть вероятность, что эти реализации могут доставлять дейтаграммы многоадресной передачи приложениям, не знающим о многоадресной передаче. Более новые ядра требуют, чтобы процесс связывался с портом и устанавливал какой-нибудь параметр сокета многоадресной передачи для сокета как указатель того, что приложение знает о многоадресной передаче. Самый обычный параметр сокета многоадресной передачи — признак присоединения к группе. Для Solaris 2.5 характерны некоторые отличия: дейтаграммы многоадресной передачи доставляются только на те сокеты, которые присоединились к группе и связались с портом. В целях переносимости все приложения многоадресной передачи должны присоединиться к группе и связаться с портом.
Более новый интерфейс многоадресного сервиса требует, чтобы уровень IP доставлял многоадресные пакеты сокету только в том случае, если этот сокет присоединился к группе или источнику. Такое требование было введено с IGMPv3 (RFC 3376 [16]), чтобы разрешить фильтрацию источников и многоадресную передачу от источника. Таким образом ужесточается требование на присоединение к группе, но зато ослабляется требование на связывание группового адреса. Однако для наибольшей переносимости со старыми и новыми интерфейсами приложения должны присоединяться к группам и связывать сокеты с групповыми адресами.
Некоторые более старые узлы, имеющие возможность многоадресной передачи, не позволяют связывать адрес многоадресной передачи с сокетом при помощи функции bind. В целях переносимости приложение может игнорировать ошибку функции bind при связывании адреса многоадресной передачи с сокетом и делать повторную попытку с адресом INADDR_ANY или in6addr_any.
21.7. Функция mcast_join и родственные функции
Несмотря на то что параметры сокетов многоадресной передачи для IPv4 аналогичны параметрам сокетов многоадресной передачи для IPv6, есть достаточно много различий, из-за которых не зависящий от протокола код, использующий многоадресную передачу, усложняется и содержит множество директив
#ifdef
. Наилучшим решением будет использование приведенных ниже восьми функций, позволяющих скрыть различия реализаций:
#include "unp.h"
int mcast_join(int <i>sockfd</i>, const struct sockaddr *<i>grp</i>,
socklen_t <i>grplen</i>, const char *<i>ifname</i>, u_int <i>ifindex</i>);
int mcast_leave(int <i>sockfd</i>, const struct sockaddr *<i>grp</i>,
socklen_t <i>grplen</i>);
int mcast_block_source(int <i>sockfd</i>,
const struct sockaddr *<i>src</i>, socklen_t <i>srclen</i>,
const struct sockaddr *<i>grp</i>, socklen_t <i>grplen</i>);
int mcast_unblock_source(int <i>sockfd</i>,
const struct sockaddr *<i>src</i>, socklen_t <i>srclen</i>,
const struct sockaddr *<i>grp</i>, socklen_t <i>grplen</i>);
int mcast_join_source_group(int <i>sockfd</i>,
const struct sockaddr *<i>src</i>, socklen_t <i>srclen</i>,
const struct sockaddr *<i>grp</i>, socklen_t <i>grplen</i>,
const char *<i>ifname</i>, u_int <i>ifindex</i>);
int mcast_leave_source_group(int <i>sockfd</i>,
const struct sockaddr *<i>src</i>, socklen_t <i>srclen</i>,
const struct sockaddr *<i>grp</i>, socklen_t <i>grplen</i>);
int mcast_set_if(int <i>sockfd</i>, const char *<i>ifname</i>, u_int <i>ifindex</i>);
int mcast_set_loop(int <i>sockfd</i>, int <i>flag</i>);
int mcast_set_ttl(int <i>sockfd</i>, int <i>ttl</i>);
<i>Все перечисленные выше функции возвращают: 0 в случае успешного выполнения, -1 в случае ошибки</i>
int mcast_get_if(int <i>sockfd</i>);
<i>Возвращает: неотрицательный индекс интерфейса в случае успешного выполнения, -1 в случае ошибки</i>
int mcast_get_loop(int <i>sockfd</i>);
<i>Возвращает: текущий флаг закольцовки в случае успешного выполнения, -1 в случае ошибки</i>
int mcast_get_ttl(int <i>sockfd</i>);
<i>Возвращает: текущее значение TTL или предельное количество транзитных узлов в случае успешного выполнения, -1 в случае ошибки</i>
Функция
mcast_join
присоединяет узел к группе. IP-адрес этой группы содержится в структуре адреса сокета, на которую указывает аргумент
grp
, а длина этой структуры задается аргументом
grplen
. Мы можем задать интерфейс, на котором должно происходить присоединение к группе, либо через имя интерфейса (непустой аргумент
ifname
), либо через ненулевой индекс интерфейса (непустой аргумент
ifindex
). Если ни одно из этих значений не задано, ядро самостоятельно выбирает интерфейс, на котором происходит присоединение к группе. Вспомните, что в случае IPv6 для работы с параметрами сокета интерфейс задается по его индексу. Если для сокета IPv6 известно имя интерфейса, нужно вызвать функцию
if_nametoindex
, чтобы получить индекс интерфейса. В случае параметра сокета IPv4 мы задаем интерфейс по его IP-адресу направленной передачи. Если для сокета IPv4 интерфейс задан по имени, нужно вызвать функцию
ioctl
с запросом
SIOCGIFADDR
для получения IP-адреса направленной передачи для этого интерфейса. Если для сокета IPv4 задан индекс интерфейса, мы сначала вызываем функцию
if_indextoname
, чтобы получить имя интерфейса, а затем обрабатываем имя так, как только что было сказано.