Получение списка интерфейсов
20-22
Наша функция get_ifi_info
возвращает информацию обо всех интерфейсах и адресах. Запрашиваемое нами семейство адреса берется из структуры адреса сокета, заполненной функцией udp_client
на основе аргумента командной строки.
Присоединение к группе
23-27
Мы вызываем нашу функцию mcast_join
, чтобы присоединиться к группе, заданной аргументом командной строки для каждого интерфейса, поддерживающего многоадресную передачу. Все эти присоединения происходят на одном сокете, который использует эта программа. Как отмечалось ранее, количество присоединений на одном сокете ограничено константой IP_MAX_MEMBERSHIPS
(которая обычно имеет значение 20), но лишь немногие многоинтерфейсные узлы используют столько интерфейсов.
Чтение и обработка всех пакетов NTP
30-36
В памяти размещается другая структура адреса сокета для хранения адреса, возвращаемого функцией recvfrom
, и программа входит в бесконечный цикл, считывая все пакеты NTP, которые получает узел, и вызывая нашу функцию sntp_proc
(описывается далее) для обработки пакета. Поскольку сокет был связан с универсальным адресом и присоединение к группе произошло на всех интерфейсах, поддерживающих многоадресную передачу, сокет должен получить любой пакет NTP направленной, широковещательной или многоадресной передачи, получаемый узлом. Перед вызовом функции sntp_proc
мы вызываем функцию gettimeofday
, чтобы получить текущее время, потому что функция sntp_proc
вычисляет разницу между временем пакета и текущим временем.
Наша функция sntp_proc
, показанная в листинге 21.13, обрабатывает пакет NTP.
Листинг 21.13. Функция sntp_proc: обработка пакета NTР
//ssntp/sntp_proc.c
1 #include "sntp.h"
2 void
3 sntp proc(char *buf, ssize_t n, struct timeval *nowptr)
4 {
5 int version, mode;
6 uint32_t nsec, useci;
7 double usecf;
8 struct timeval diff;
9 struct ntpdata *ntp;
10 if (n < (ssize_t)sizeof(struct ntpdata)) {
11 printf("\npacket too small: %d bytes\n", n);
12 return;
13 }
14 ntp = (struct ntpdata*)buf;
15 version = (ntp->status & VERSION_MASK) >> 3;
16 mode = ntp->status & MODE_MASK;
17 printf("\nv%d, mode %d, strat %d, ", version, mode, ntp->stratum);
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 (2 32). Результат больше либо равен 0.0 и меньше 1.0. Мы умножаем это число на 1 000 000 — число микросекунд в секунде, записывая результат в переменную useci
как 32-разрядное целое без знака.
Число микросекунд лежит в интервале от 0 до 999 999 (см. упражнение 21.5). Мы преобразуем значение в микросекунды, поскольку отметка времени Unix, возвращаемая функцией gettimeofday
, возвращается как два целых числа: число секунд и число микросекунд, прошедшее с 1 января 1970 года (UTC). Затем мы вычисляем и выводим разницу между истинным временем узла и истинным временем сервера NTP в микросекундах.
Один из факторов, не учитываемых нашей программой, — это задержка в сети между клиентом и сервером. Но мы считаем, что пакеты NTP обычно приходят как широковещательные или многоадресные пакеты в локальной сети, а в этом случае задержка в сети составит всего несколько миллисекунд.
Читать дальше
Конец ознакомительного отрывка
Купить книгу