Литмир - Электронная Библиотека
Содержание  
A
A

18  if (mode == MODE_CLIENT) {

19   printf("client\n");

20   return;

21  }

22  nsec = ntohl(ntp->xmt.int_part) - JAN_1970;

23  useci = ntohl(ntp->xmt.fraction); /* 32-разрядная дробь */

24  usecf = useci; /* дробь в double */

25  usecf /= 4294967296.0; /* деление на 2**32 -> [0, 1.0) */

26  useci = usecf * 1000000.0; /* дробь в миллионную часть */

27  diff.tv_sec = nowptr->tv_sec - nsec;

28  if ((diff.tv_usec = nowptr->tv_usec - useci) < 0) {

29   diff.tv_usec += 1000000;

30   diff.tv_sec--;

31  }

32  useci = (diff.tv_sec * 1000000) + diff.tv_usec; /* diff в мс */

33  printf("clock difference = %d usec\n", useci);

34 }

Ратификация пакета

10-21
 Сначала мы проверяем размер пакета, затем выводим его версию, режим и слой (stratum) сервера. Если режимом является
MODE_CLIENT
, пакет является запросом клиента, а не ответом сервера, и мы игнорируем его.

Получение времени передачи из пакета NTP

22-34
 В пакете NTP нас интересует поле
xmt
— отметка времени. Это 64-разрядное значение с фиксированной точкой, определяющее момент отправки пакета сервером. Поскольку отметки времени NTP отсчитывают секунды начиная с 1 января 1900 года, а отметки времени Unix — с 1 января 1970 года, сначала мы вычитаем
JAN_1970
(число секунд в 70 годах) из целой части.

Дробная часть — это 32-разрядное целое без знака, которое может принимать значение от 0 до 4 294 967 295 включительно. Оно копируется из 32-разрядного целого (

usecf
) в переменную с плавающей точкой двойной точности (
usecf
) и делится на 4 294 967 296 (232). Результат больше либо равен 0.0 и меньше 1.0. Мы умножаем это число на 1 000 000 — число микросекунд в секунде, записывая результат в переменную
useci
как 32-разрядное целое без знака.

Число микросекунд лежит в интервале от 0 до 999 999 (см. упражнение 21.5). Мы преобразуем значение в микросекунды, поскольку отметка времени Unix, возвращаемая функцией

gettimeofday
, возвращается как два целых числа: число секунд и число микросекунд, прошедшее с 1 января 1970 года (UTC). Затем мы вычисляем и выводим разницу между истинным временем узла и истинным временем сервера NTP в микросекундах.

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

Если мы запустим эту программу на узле

macosx
с сервером NTP на узле
freebsd4
, который с помощью многоадресной передачи отправляет пакеты NTP в сеть Ethernet каждые 64 с, то получим следующий результат:

macosx # <b>ssntp 224.0.1.1</b>

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-адрес отправителя посылаемой вами дейтаграммы?

242
{"b":"225366","o":1}