UNIX: разработка сетевых приложений - Уильям Стивенс
Шрифт:
Интервал:
Закладка:
Один из факторов, не учитываемых нашей программой, — это задержка в сети между клиентом и сервером. Но мы считаем, что пакеты NTP обычно приходят как широковещательные или многоадресные пакеты в локальной сети, а в этом случае задержка в сети составит всего несколько миллисекунд.
Если мы запустим эту программу на узле macosx с сервером NTP на узле freebsd4, который с помощью многоадресной передачи отправляет пакеты NTP в сеть Ethernet каждые 64 с, то получим следующий результат:
macosx # ssntp 224.0.1.1
joined 224.0.1.1.123 on lo0
joined 224.0.1.1.123 on en1
v4, mode 5, strat 3, clock difference = 661 usec
v4, mode 5, strat 3, clock difference = -1789 usec
v4, mode 5, strat 3, clock difference = -2945 usec
v4, mode 5, strat 3, clock difference = -3689 usec
v4, mode 5, strat 3, clock difference = -5425 usec
v4, mode 5, strat 3, clock difference = -6700 usec
v4, mode 5, strat 3, clock difference = -8520 usec
Перед запуском нашей программы мы завершили на узле работу NTP-сервера, поэтому когда наша программа запускается, время очень близко к времени сервера. Мы видим, что этот узел отстал на 9181 мс за 384 с работы программы, то есть за 24 ч он отстанет на 2 с.
21.12. Резюме
Для запуска приложения многоадресной передачи в первую очередь требуется присоединиться к группе, заданной для этого приложения. Тем самым уровень IP получает указание присоединиться к группе, что, в свою очередь, указывает канальному уровню на необходимость получать кадры многоадресной передачи, отправляемые на соответствующий адрес многоадресной передачи аппаратного уровня. Многоадресная передача использует преимущество аппаратной фильтрации, имеющееся у большинства интерфейсных карт, и чем качественнее фильтрация, тем меньше число нежелательных получаемых пакетов. Использование аппаратной фильтрации сокращает нагрузку на все узлы, не задействованные в приложении.
Многоадресная передача в глобальной сети требует наличия маршрутизаторов, поддерживающих многоадресную передачу, и протокола маршрутизации многоадресной передачи. Поскольку не все маршрутизаторы в Интернете имеют возможность многоадресной передачи, для этой цели используется IP-инфраструктура многоадресной передачи.
API для многоадресной передачи обеспечивают девять параметров сокетов:
■ присоединение к группе на интерфейсе;
■ выход из группы;
■ блокирование передачи от источника;
■ разблокирование заблокированного источника;
■ присоединение интерфейса к группе многоадресной передачи от источника;
■ выход из группы многоадресной передачи от источника;
■ установка интерфейса по умолчанию для исходящих пакетов многоадресной передачи;
■ установка значения TTL или предельного количества транзитных узлов для исходящих пакетов многоадресной передачи;
■ включение или отключение закольцовки для пакетов многоадресной передачи.
Первые шесть параметров предназначены для получения пакетов многоадресной передачи, последние три — для отправки. Существует достаточно большая разница между указанными параметрами сокетов IPv4 и IPv6. Вследствие этого код многоадресной передачи, зависящий от протокола, очень быстро становится «замусорен» директивами #ifdef. Мы разработали 12 наших собственных функций с именами, начинающимися с mcast_, для упрощения написания приложений многоадресной передачи, работающих как с IPv4, так и с IPv6.
Упражнения
1. Скомпилируйте программу, показанную в листинге 20.5, и запустите ее, задав в командной строке IP-адрес 224.0.0.1. Что произойдет?
2. Измените программу из предыдущего примера, чтобы связать IP-адрес 224.0.0.1 и порт 0 с сокетом. Запустите ее. Разрешается ли вам связывать адрес многоадресной передачи с сокетом при помощи функции bind? Если у вас есть такая программа, как tcpdump, понаблюдайте за пакетами в сети. Каков IP-адрес отправителя посылаемой вами дейтаграммы?
3. Один из способов определить, какие узлы в вашей подсети имеют возможность многоадресной передачи, заключается в запуске утилиты ping для группы всех узлов, то есть для адреса 224.0.0.1. Попробуйте это сделать.
4. Одним из способов обнаружения маршрутизаторов многоадресной передачи в вашей подсети является запуск утилиты ping для группы всех маршрутизаторов — 224.0.0.2. Попробуйте это сделать.
5. Один из способов узнать, соединен ли ваш узел с многоадресной IP-инфраструктурой — запустить нашу программу из раздела 21.9, подождать несколько минут и посмотреть, появляются ли анонсы сеанса. Попробуйте сделать это и посмотрите, получите ли вы какие-нибудь анонсы.
6. Выполните вычисления в листинге 21.12 при условии, что дробная часть отметки времени NTP равна 1 073 741 824 (одна четвертая от 232).
Выполните еще раз эти же вычисления для максимально возможной дробной части (232 - 1).
Измените реализацию функции mcast_set_if для IPv4 так, чтобы запоминать имя каждого интерфейса, для которого она получает IP-адрес. Это позволит избежать нового вызова функции ioctl для данного интерфейса.
Глава 22
Дополнительные сведения о сокетах udp
22.1. Введение
Эта глава объединяет различные темы, касающиеся приложений, использующих сокеты UDP. Для начала нас интересует, как определяется адрес получателя дейтаграммы UDP и интерфейс, на котором дейтаграмма была получена, поскольку сокет, связанный с портом UDP и универсальным адресом, может получать дейтаграммы направленной, широковещательной и многоадресной передачи на любом интерфейсе.
TCP — это потоковый протокол, использующий окно переменной величины (sliding window), поэтому в TCP отсутствует такое понятие, как граница записи, и невозможно переполнение буфера получателя отправителем в результате передачи слишком большого количества данных. Однако в случае UDP каждой операции ввода соответствует одна дейтаграмма UDP (запись), поэтому возникает вопрос: что произойдет, когда полученная дейтаграмма окажется больше приемного буфера приложения?
UDP — это ненадежный протокол, однако существуют приложения, в которых UDP использовать целесообразнее, чем TCP. Мы рассмотрим факторы, под влиянием которых UDP оказывается предпочтительнее TCP. В UDP-приложения необходимо включать ряд функций, в некоторой степени компенсирующих ненадежность UDP: тайм-аут и повторную передачу, обработку потерянных дейтаграмм и порядковые номера для сопоставления ответов запросам. Мы разработаем набор функций, которые сможем вызывать из наших приложений UDP.
Если реализация не поддерживает параметр сокета IP_RECVDSTADDR, один из способов определить IP-адрес получателя UDP-дейтаграммы заключается в связывании всех интерфейсных адресов и использовании функции select.
Большинство серверов UDP являются последовательными, но существуют приложения, обменивающиеся множеством дейтаграмм UDP между клиентом и сервером, что требует параллельной обработки. Примером может служить TFTP (Trivial File Transfer Protocol — упрощенный протокол передачи файлов). Мы рассмотрим два варианта подобного согласования — с использованием суперсервера inetd и без него.
В завершение этой главы мы рассмотрим информацию о пакете, которая может быть передана во вспомогательных данных дейтаграммы IPv6: IP-адрес отправителя, отправляющий интерфейс, предельное количество транзитных узлов исходящих дейтаграмм и адрес следующего транзитного узла. Аналогичная информация — IP-адрес получателя, принимающий интерфейс и предельное количество транзитных узлов — может быть получена вместе с дейтаграммой IPv6.
22.2. Получение флагов, IP-адреса получателя и индекса интерфейса
Исторически функции sendmsg и recvmsg использовались только для передачи дескрипторов через доменные сокеты Unix (см. раздел 15.7), но даже это происходило сравнительно редко. Однако в настоящее время популярность этих двух функций растет по двум причинам:
1. Элемент msg_flags, добавленный в структуру msghdr в реализации 4.3BSD Reno, возвращает приложению флаги сообщения. Эти флаги мы перечислили в табл. 14.2.
2. Вспомогательные данные используются для передачи все большего количества информации между приложением и ядром. В главе 27 мы увидим, что IPv6 продолжает эту тенденцию.
В качестве примера использования функции recvmsg мы напишем функцию recvfrom_flags, аналогичную функции recvfrom, но дополнительно позволяющую получить:
■ возвращаемое значение msg_flags;
■ адрес получателя полученной дейтаграммы (из параметра сокета IP_RECVDSTADDR);
■ индекс интерфейса, на котором была получена дейтаграмма (параметр сокета IP_RECVIF).
Чтобы можно было получить два последних элемента, мы определяем в нашем заголовке unp.h следующую структуру: