Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Малышев_Сетевое программирование 19.12.18

.pdf
Скачиваний:
2
Добавлен:
21.11.2025
Размер:
896.22 Кб
Скачать

 

41

 

Участок с груп-

 

повой адресаци-

 

ей

 

Однонаправленный тун-

 

нель

Участок с груп-

 

повой адресаци-

Однонаправленный тун-

ей

нель

Участок с групповой адресацией

Рис. 10. Групповая рассылка через туннели

Туннелирование происходит посредством инкапсуляции групповых данных в IP-дейтаграммы. Таким образом, групповые данные передаются как обычные.

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

Web-страницу, на которой сообщается о реальном состоянии сетей в Интернете, допускающих групповую рассылку, можно найти по адресу http://www.multicasttech.com/status.

Внастоящее время МВоnе работает на DVMRP (основанном на длине вектора расстояния) и используется для групповой рассылки аудио-, видео- информации, технических обсуждений и семинаров, данных о полетах космических челноков NASA и т. д. Инструменты МВоnе (например, sdr или multikit) обеспечивают информацией о запланированных мероприятиях с групповой рассылкой.

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

42

присоединяется к группе, отправив запрос IGMP маршрутизаторам в своей локальной сети, используя IP-адрес 224.0.0.2. На рисунке только маршрутизатор Z находится в подсети клиента Е. Он регистрирует клиента как члена группы рассылки и информирует другие маршрутизаторы, используя один из протоколов групповой маршрутизации: MOSPF, PIM или DVMRP. Таким образом, маршрутизаторы, оснащенные средством групповой рассылки, передают информацию о члене этой группы (о клиенте Е) другим маршрутизаторам. От сервера лишь требуется отправить UDP- сообщение по групповому адресу. Поскольку маршрутизатор X теперь знает, что есть клиент, желающий получать эти сообщения, он переправляет групповое сообщение маршрутизатору Z, который в свою очередь направляет его в подсеть клиента Е. Таким образом, клиент Е может считать групповое сообщение. Это сообщение не попадет в сеть маршрутизатора Y, если ни один клиент этой сети не присоединился к группе рассылки.

Сервер (отправитель)

UDP-сообщение

Маршрутизатор Х

Маршрутизатор Y

Маршрутизатор Z

MOSPF, PIM, DVMRP

IGMP

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Клиент А

Клиент В

 

 

Клиент D

Клиент E

Клиент С

 

 

Рис. 11. Пересылка группового пакета

6.5. ПРОТОКОЛЫ ГРУППОВОЙ МАРШРУТИЗАЦИИ

Для перенаправления докладов о членстве в группах и поиска наилучшего пути от отправителя до получателя маршрутизаторами таким образом используются протоколы групповой маршрутизации. Когда создавалась магистраль МВоnе, использовался единственный протокол Distance

43

Vector Multicast Routing Protocol (DVMRP). В настоящее время для группо-

вой рассылки широкое распространение получили другие протоколы: Multicast Open Shortest Path First (MOSPF) и Protocol Independent Multicast

(PIM) [9].

Впротоколе DVMRP применяется алгоритм лавинной маршрутизации по обратным путям, в котором маршрутизатор посылает копию пакета по всем путям, за исключением входящего. Если в сети какого-то маршрутизатора нет узлов, входящих в группу рассылки, то маршрутизатор отправляет отсекающее сообщение обратно маршрутизатору-отправителю, чтобы тот знал, что ему не нужно отсылать пакеты для этой группы рассылки в этом направлении (маршрутизатор Y отправляет отсекающее сообщение маршрутизатору Х). Поэтому DVMRP не слишком хорошо масштабируется.

Протокол MOSPF представляет собой расширение протокола OSPF (Open Shortest Path First). При использовании MOSPF все маршрутизаторы должны знать о наличии связей с сетями, содержащими члены групп рассылки. MOSPF (протокол состояния канала) вычисляет маршруты при получении трафика групповой рассылки. Поскольку маршрутизаторы обмениваются маршрутами MOSPF, используя OSPF, то MOSPF может использоваться только в сетях, в которых в качестве протокола одноадресной маршрутизации используется OSPF. Кроме того, MOSPF не будет хорошо масштабироваться, если используется много групп рассылки или группы часто изменяются, поскольку при этом будет поглощаться значительная вычислительная мощность маршрутизаторов.

Впротоколе PIM для отправки сообщений членам группы используются два разных алгоритма. Если члены группы широко разбросаны по множеству сетей, применяется алгоритм PIM-SM (растянутый режим), а если группа использует лишь несколько сетей, применяется алгоритм PIM-DM (плотный режим). В плотном режиме применяется лавинная маршрутизация по обратным путям, похожая на DVMRP, но может использоваться любой однонаправленный протокол маршрутизации. Растянутый режим PIM-SM определяет точку регистрации для надлежащей маршрутизации пакетов.

6.6.ОСОБЕННОСТИ РАБОТЫ С MULTICAST

Опишем работу с технологией групповой передачи данных, или иначе UDP Multicast, и особенностью, которая возникает при написании кроссплатформенного кода [23].

44

Вся работа начинается с создания сокета и его «настройки»:

1)создать сокет

2)сделать bind

3)подключится с Multicast группе.

Создание сокета

sockfd = socket(AF_INET, SOCK_DGRAM, 0);

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

Первое, что необходимо сделать, это позволить использовать PORT

повторно, т. к. помимо кто-то еще может работать с этим портом. const int optval = 1;

setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

Функция setsockopt позволяет задать опции для сокета. Значение опции передается по указателю на void, т. к. некоторые опции требует не просто флаг включено/выключено, а структуры с дополнительными данными.

Далее необходимо связать сокет с портом. Тут возникает первая особенность – различная идеология ядер Windows и Linux. А именно то, что под Windows нельзя привязаться (bind) на адрес Multicast группы (получим ошибку) и должны привязываться на INADDR_ANY

struct sockaddr_in addr; bzero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port);

addr.sin_addr.s_addr = htonl(INADDR_ANY);

bind(sockfd, (sockaddr *)&addr, sizeof(addr)); // связывает сокет с

//локальным адресом

На Linux можно так же привязаться на INADDR_ANY, но в этом случае будут получены все дейтаграммы, пришедшие на привязанный порт. Для того чтобы получать дейтаграммы только из нужной группы, необходимо привязаться именно на адрес этой группы (и порт).

Подключение к группе

Главной особенностью Multicast является то, что компьютер не будет получать данных до тех пор, пока нет подключения к Multicast группе. Подключение на уровне пользователя выглядит как установка опции для сокета [23].

struct ip_mreq mreq;

inet_aton(ip_addr, &(mreq.imr_multiaddr)); mreq.imr_interface.s_addr = htonl(INADDR_ANY);

45

setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

где ip_mreq.imr_multiaddr - это адрес мультикаст группы. А ip_mreq.imr_interface - это адрес интерфейса, на котором ожидается получение дейтаграмм. INADDR_ANY в данном случае говорит о том, что оставляем это право за ядром, которое выберет интерфейс исходя из таблицы роутинга. Далее ядро проверит не подключены ли мы уже к этой группе, и если нет, то отправит запрос на ближайший Multicast-сервер. Тот в свою очередь дальше и т. д. После чего на компьютер будут отправлять дейтаграммы, отправляемые в данную Multicast-группу.

При привязке сокета на INADDR_ANY ядро Windows само производит фильтрацию получаемых данных. И таким образом на сокет доставляются данные только из подписанных групп.

На Linux для того, чтобы получать данные на сокет только от подключенной Multicast группы, необходимо забиндиться на ее же адрес.

Чтобы получить дейтаграмму многоадресной передачи, процесс должен присоединиться к группе, а также связать при помощи функции bind сокет UDP с номером порта, который будет использоваться как номер порта получателя для дейтаграмм, отсылаемых данной группе.

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

В boost (собрание библиотек классов, использующих функциональность языка C++ и предоставляющих удобный кроссплатформенный высокоуровневый интерфейс для лаконичного кодирования различных повседневных подзадач программирования.) все аналогично API, никакой адаптации под единую логику не производится. Таким образом, используя его для написание кроссплатформенного приложение все равно придется делать следующее:

#ifdef WIN32

boost::asio::ip::udp::endpoint listen_endpoint("0.0.0.0", multicast_port); #else

boost::asio::ip::udp::endpoint listen_endpoint(multicast_address, multicast_port);

#endif socket_.open(listen_endpoint.protocol());

46

socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true)); socket_.bind(listen_endpoint); ocket_.set_option(boost::asio::ip::multicast::join_group(multicast_addres

s));

Несколько групп для одного сокета

Путь, предложенный для Linux, скрывает одно ограничение. Должны работать по принципу один сокет – одна Multicast-группа, так как вряд ли возможно привязаться сразу не несколько адресов [23].

Есть сервер, он вещает данные в несколько мультикаст-групп на один порт.

Есть клиент, он желает получать от сервера данные только в нескольких группах. Тогда ему нужно предпринять следующие действия:

1)привязаться на INADDR_ANY;

2)далее фильтровать все полученные дейтаграммы вручную, определяя их адрес назначения.

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

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

Но при этом возникает проблема избыточности данных.

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

Групповая передача дейтаграмм на Java

Протокол UDP позволяет отправить одно сообщение нескольким адресатам. Передача сообщения может происходить как в режиме группового вещания, так и в режиме широковещания. Последний на Java не поддерживается [24].

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

47

Групповой сокет представляется в Java объектом класса MulticastSocket, у которого есть два конструктора:

MulticastSocket ms = new MulticastSocket(); MulticastSocket ms = new MuiticastSocket(intlocalPort);

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

Когда групповой сокет создан, он ведет себя так же, как и обычный

UDP сокет (объект класса DatagramSocket).

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

пового вещания: 224.0.0.0 239.255.255.255. MulticastSocket ms = new MulticastSocket(16900); ms.joinGroup(InetAddress.getByName ("224.0.0.1"));

ms.joinGroup(InetAddress.getByName("228.58.120.11"));

С этого момента сокет будет получать сообщения, посланные по ад-

ресам 224.0.0.1:16900 и 228.58.120.11:16900.

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

Стандарт IPv6 поддерживает групповую передачу UDP сообщений и планирует реализовать многоадресную доставку TCP-пакетов. Teм самым проблема ненадежности протокола UDP будет решена. Но дело в том, что класс MulticastSocket порожденот класса DatagramSocket, по-этому не может быть адаптирован к грядущим изменениям.

Создавая иерархию объектов, лучше оставить место для последующих изменений или расширений, чем потом переделывать всю иерархию.

В листинге 2 показано, как создать и сконфигурировать групповой сокет.

Листинг 2. Создание группового сокета, привязка его к порту 16900, присоединение к группе и ожидание сообщений [24].

//Простейший получатель групповых сообщений (из файла

//SimpleMulticastDestination.Java)

MulticastSocket s = new

MulticastSocket{16900); // Создаем сокет

48

ms.joinGroup(InetAddress.getByName("224.0.0.1")); // присоединение

// к группе

String msg; do

{

byte[] line = new byte[100];

DatagramPacket pkt = new DatagramPacket(line, line.length); ms.receive(pkt);

msg = new String(pkt.getData()); System.out.println("From "+pkt.getAddress()+'4"tmsg.trim());

}

while (!msg.trim().equalsf"close") ); ms.close(); // закрываем соединение

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

Чтобы подключиться к группе, необходимо вызвать функцию setsockopt(), передав ей в качестве параметра структуру ipjnreq:

/*** Структура ipjnreq для задания группового адреса*/ struct ip_mreq

{

struct in_addr imrjnultiaddr; /* известная адресная группа */ struct in_addr imr_interface; /* сетевой интерфейс */

};

Поле imrjnultiaddr задает адресную группу, к которой необходимо присоединиться. Формат его такой же, как и у поля sin_addr структуры sockaddr_in. Поле imr_interface позволяет выбрать конкретный сетевой интерфейс узла. Это напоминает функцию bind(), которая делает то же самое, а если в качестве адреса указать константу INADDR_ANY, то сообщения будут приниматься через любой доступный интерфейс. Однако в многоадресном режиме эта константа имеет не сколько иной смысл.

Стандартный интерфейс группового вещания

Если в поле imr_interfасе присутствует константа INADDR_ANY, ядро самостоятельно выберет сетевой интерфейс. По крайней мере, в ядре Linux версий 2.2.хх эта константа не означает "прослушивать все сетевые интерфейсы". Поэтому, если в системе есть несколько сетевых устройств, подключаться к группе необходимо по каждому устройству в отдельности.

49

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

Поле imr_interface задано равным INADD_ANY исключительно в демонстрационных целях. Так можно делать только в том случае, когда в системе имеется один единственный сетевой интерфейс, иначе результаты будут непредсказуемыми.

/*** Подключение к адресной группе ***/

const char *GroupID = "224.0.0.10"; struct ip_mreq mreq;

if ( inet _aton(GroupID, &mreq.imr_multiaddr) == 0 ) panic("address ( % s ) bad", GroupID); mreq.imrinterface.s_addr=INADDR_ANY;

if (setsockopt(sd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0 )

panic("Join multicast failed");

Если предполагается подключение другой программы по тому же самому адресу, необходимо установить параметр сокета SO_REUSEADDR.

В настоящее время в Linux не поддерживается совместное использо-

вание портов (SO_REUSEPORT).

/*** Включение режима совместного использования адресов ***/

if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))

!= 0 )

panic("Can't reuse address/ports");

Число групп, к которым можно подключаться, зависит от аппаратных ограничений и настроек ядра. Во многих версиях UNIX допускается 16 подключений от одного компьютера. Да, именно от компьютера, а не от программы.

Когда прослушивание порта закончено, нужно выйти из состава группы:

/*** Выход из состава адресной группы ***/

if (setsockopt(sd, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) != 0 )

panic("Drop multicast failed");

Можно просто выйти из программы, и система автоматически разорвет все соединения.

Очистка групповых соединений Многоадресный режим – один из тех немногих случаев, когда лучше

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

50

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

Подключиться к многоадресной группе достаточно легко. По сути, для создания локальной группы достаточно выбрать неиспользуемый IP- адрес.

Иное дело глобальные адреса. Чтобы опубликовать сервис группового вещания в Internet, необходимо запросить адрес и порт у организации

IAB (Internet Architecture Board – архитектурный совет сети Internet).

Отправка многоадресного сообщения Послать сообщение группе адресатов так же легко, как и отправить

дейтаграмму. Для UDP подсистемы достаточно указать адрес, а все остальное осуществляется автоматически.

6.7.ШИРОКОВЕЩАТЕЛЬНАЯ ПЕРЕДАЧА

При выполнении приложения можно указывать отдельный IP-адрес, по которому можно отправить, например файл, но также можно задать широковещательный адрес, чтобы отправить информацию всем компьютерам подсети или всей сети [9]. Широковещательный адрес состоит из адреса (под) сети и единиц во всех остальных битах. Это IP-адреса, в котором все биты, относящиеся к адресу компьютера, установлены в 1. Например, чтобы отправить сообщения всем ПК подсети с адресом 192.168.0 с маской 255.255.255.0, нужно задать широковещательный адрес 192.168.0.255. Тогда любой ПК с IP-адресом, начинающимся с 192.168.0, будет получать широковещательные сообщения (сеть 192.168, маска 255.255.0.0, широковещательный адрес 192.168.255.255). Чтобы отправить сообщение всем ПК сети независимо от маски подсети, можно использо-

вать адрес 255.255.255.255.

При широковещательной передаче сообщение отправляется всем ПК сети, а клиент решает, хочет ли он обрабатывать эти данные или проигнорировать.

Широковещательные сообщения всегда передаются без установления соединений с использованием протокола UDP [9]. Сервер может отправить данные независимо от того, слушает ли его хоть один клиент. По соображениям производительности было бы невозможно установить отдельное соединение с каждым отдельным клиентом (по TCP). Взаимодействие без установления соединения (по UDP) означает, что сервер не дол-