6.1 KiB
NTP Node Time
1. Назначение
Обмен временем с прямыми ETCP-пирами. Если у узла ещё нет поправки, он принимает время синхронизированного пира с оценкой сетевой задержки. Поправка хранится в instance->ntp и применяется getters из ntp_time.h; часы ОС не меняются. Отключение интернет-NTP (ntp.enabled=0) не отключает этот обмен. Уже синхронизированный узел повторно не принимает поправку от пиров, но интернет-NTP может её обновить.
2. Как пользоваться
В uasync-потоке после создания instance->connections вызвать ntp_node_time_init(). Он привязывает raw packet ID ETCP_ID_NTP_TIME = 0x04 (прямой ETCP, без роутера), подписывается на новые соединения и на INIT всех существующих, в том числе pending. На INIT отправляется TIME_SYNC; при запуске модуля пакет также отправляется уже инициализированным соединениям. Повторный активный init возвращает 0 без дублирования подписок. При занятом ID или ошибке регистрации возвращается -1; свои частичные регистрации снимаются.
TIME_SYNC — 10 байт: ID (1), flags (1, bit 0 = synced), время отправителя (int64 Unix мкс в нативном порядке байтов). Синхронизированный отправитель передаёт скорректированное время, остальные — локальное. При первом сообщении сохраняется исходное смещение для диагностики. Если получатель не синхронизирован, а отправитель синхронизирован, применяется:
offset_us = local_receive_us - sender_us - mean_link_rtt_us / 2.
Среднее RTT — арифметическое по всем элементам conn->links, включая нулевые значения и down-линки; rtt_last переводится из 0,1 мс в микросекунды. При отсутствии линков задержка считается нулевой. Это приближённая оценка: симметричность пути и точность часов пира не гарантируются.
После принятия поправки устанавливаются ntp.synced и ntp.last_sync_tb, затем время рассылается остальным. Синхронизированный получатель отвечает несинхронизированному отправителю. Когда оба синхронизированы, ответа нет — бесконечный обмен не возникает. Полученный entry и его dgram освобождаются при обработке, в том числе при заполненном кеше диагностики.
ntp_node_sync_peers() рассылает время по соединениям с initialized && links_up, если модуль активен и есть поправка. Вызывается также после успешной интернет-NTP-синхронизации.
Нюансы:
- Кеш содержит до 32 диагностических записей, а не ограничивает число соседей. При заполнении вытесняется запись с наименьшим
last_seen_tb(при равенстве — первая по индексу); исходное смещение новой записи сбрасывается. - Диагностическое смещение —
raw_local_receive_us - sender_us, без вычитания задержки. Оно отличается от глобальной поправки, принятой с оценкой половины RTT. - При повторном сообщении модуль сравнивает смещение с предыдущим. Модуль разности строго >2 с даёт WARN, строго >10 с — ERROR. Это изменение относительного смещения: оно может включать коррекцию часов отправителя и изменение задержки, а не только дрейф часов.
destroyвызывается в uasync-потоке вне callbacks модуля/соединений. Снимает raw binding, подписку новых соединений и свои callbacks со всех текущих соединений, очищает кеш. Глобальную поправку не меняет. Повторный вызов и NULL безопасны.
3. API
Структуры:
struct NTP_NODE_PEER— ID пира, предыдущее диагностическое смещение, наличие исходного значения и время последнего обновления.struct NTP_NODE_TIME— кеш, число занятых записей и флаг активности; контракты полей/функций описаны вntp_node_time.h.
Функции:
ntp_node_time_init(inst)— 0 при успехе/уже активном модуле, -1 при ошибке; inst и очередь connections обязательны.ntp_node_time_destroy(inst)— снимает регистрации и очищает кеш, сохраняя глобальную поправку.ntp_node_sync_peers(inst)— рассылает время готовым прямым соседям; для NULL/неактивного/несинхронизированного модуля ничего не делает.