Кто-нибудь скажет, а что тут интересного: для Linux ntpdate, для Windows net time. Но речь пойдет о встраиваемых устройствах без ОС, но со стеком TCP/IP. В таком случае реализовать способ коррекции придется самостоятельно.
Сразу оговоримся, что не будем ставить задачу обеспечить очень высокую точность коррекции, нам достаточно будет в пределах времени отклика от сервера (ping). Это обычно менее 1 сек. При изучении данного вопроса выяснил, что коррекцию можно провести 4-мя способами:
- Запросом к любому web-серверу.
- Запросом к серверу NTP, протокол DAYTIME.
- Запросом к серверу NTP, протокол TIME.
- Запросом к серверу NTP, протокол NTP.
Рассмотрим подробнее.
Показать/спрятать
1. Использование HTTP.
В HTTP-заголовке ответа сервера всегда присутствует строка DATA. В качестве запроса удобно использовать HEAD - будет получен только заголовок, что нам и нужно. Пример:Запрос: HEAD / HTTP/1.1\r\n Host: yandex.ru\r\n \r\n Ответ: HTTP/1.1 302 Found Date: Thu, 15 Aug 2013 19:08:41 GMT Location: http://www.yandex.ru/ Vary: Accept-Encoding Content-Type: text/html; charset=iso-8859-1 Transfer-Encoding: chunkedДостоинства - простота, очень большой выбор серверов. Недостатки - при наличии специализированных NTP серверов как-то не солидно.
2. Использование DAYTIME.
Протокол DAYTIME - простейший протокол для получения значения точного времени. Чтобы получить время, нужно отправить на порт 13 NTP-сервера пустой TCP пакет. В ответ придет текстовая строка, содержащая в себе время. Строка предназначена для чтения непосредственно человеком и её формат не стандартизован. Пример ответа сервера time.nist.gov:56519 13-08-15 19:45:44 50 0 0 275.4 UTC(NIST) *Достоинства - простота. Недостатки - трудности машинной обработки ответа, малый выбор серверов. Очень небольшое количество NTP-серверов поддерживают протокол DAYTIME.
3. Использование TIME.
Протокол TIME - похож на DAYTIME, но выдает ответ, удобный для машинной обработки. Ответ состоит из 4-х байт, содержит 32-х битное беззнаковое число, соответствующе числу прошедших с 1 января 1900 г. секунд по UTC. Число записано в формате Big-Endian (первым идет старший байт). Чтобы получить ответ, надо послать на порт 37 NTP-сервера пустой TCP или UDP пакет. Пример ответа сервера time.nist.gov:D5 B9 E3 57Для преобразования этого числа в привычные дату/время можно перевести его в unix timestamp, вычтя из него количество секунд, прошедших с 01.01.1900 00:00:00 по 01.01.1970 00:00:00. Это 40 лет, 2208988800 сек. Чтобы unix timestamp перевести в дату/время можно воспользоваться имеющимся во многих библиотеках функциями преобразования даты/времени или использовать собственную функцию. Я, например после недолгого поиска нашел такую: Показать/спрятать
#define U32 unsigned int #define U16 unsigned short void RTCU32ToStr(char *str, U32 time) { U16 year, month, day, hour, min, sec; ldiv_t dvt; dvt = ldiv(time,60); sec = dvt.rem; dvt = ldiv(dvt.quot,60); min = dvt.rem; dvt = ldiv(dvt.quot,24); hour = dvt.rem; U32 jday = dvt.quot + 731; dvt = ldiv(jday*100, 36525); year = dvt.quot + 1968; dvt = ldiv(dvt.rem, 100); U16 N = dvt.quot + 1; U16 K; if (dvt.rem == 0) K = 1; else K = 2; if (N < 32) month = 1; else month = (36 * (K + N) / 11 + 98) / 100; day = N - 275 * month / 9 + K * ((month + 9) / 12) + 30; sprintf(str, "%02u.%02u.%04u %u:%02u:%02u", day, month, year, hour, min, sec); }
4. Использование NTP.
NTP - достаточно сложный протокол, наиболее широко используемый в настоящее время. C помощью NTP можно создать многоуровневую систему серверов времени. Уровень сервера называют Stratum. Сервер Stratum 1 - это сервер, сам содержащий эталонный источник тактовых сигналов, Stratum 2 - подстраивающий свой генератор и получающий время от Stratum 1 и т.д. Теоретически с помощью NTP можно достигнуть синхронизации времени с точностью до наносекунд.К счастью, если такой точности не требуется, можно также обойтись простейшим способом запрос - ответ. Для запроса данных о времени нужно послать на порт 123 NTP-сервера UDP-пакет, в ответ сервер также пошлет UDP-пакет. Запрос и ответ имеют одинаковый формат, содержат 11 переменных общим размером 48 байт. Все переменные также записываются в формате Big-Endian.
Сразу можно сказать, что такое большое количество переменных для цели узнать время с точностью до 1 сек излишне. В запросе достаточно указать первый байт Flags = 0xD9, все остальные можно занулить. В ответе достаточно использовать 4 байта, начиная от смещения 40 dec, это целая часть переменной Transmit Timestamp - формат такой же как ответ TIME. Пример минимального запроса:
Запрос: D9 [остальные 47 байт = 0] Ответ [32 начальных байта пропускаем] D5 B9 E3 57 [4 конечных тоже не нужны]
Достоинства - наиболее правильное решение, широкий выбор серверов.
Назначение остальных переменных в пакете NTP. Показать/спрятать
Формат NTP-пакета:
0 - нет предупреждения
1 - +1 сек (в минуте будет 61 сек)
2 - -1 сек (в минуте будет 59 сек)
3 - часы не синхронизированы
0 - зарезервировано
1 - symmetric active
2 - client
3 - server
4 - broadcast
5 - NTP control message
0 - не указан
1 - первичный, сам содержит эталон
2-255 - вторичные
Более полный вариант запроса. Показать/спрятать
Смещение от начала пакета | Название | Размер, байт |
---|---|---|
0 | Flags | 1 |
1 | Peer Clock Stratum | 1 |
2 | Peer Polling Interval | 1 |
3 | Peer Clock Precision | 1 |
4 | Root Delay | 4 |
8 | Root Dispersion | 4 |
12 | Reference ID | 4 |
16 | Reference Timestamp | 8 |
24 | Origin Timestamp | 8 |
32 | Receive Timestamp | 8 |
40 | Transmit Timestamp | 8 |
Flags
В битовых полях содержит несколько перечислимых переменных:Размещение | Название |
---|---|
XX.. .... | Leap Indicator |
..XX X... | Version number |
.... .XXX | Mode |
Leap Indicator
Код предупреждения о надвигающийся дополнительной секунде. Дополнительная секунда иногда добавляется или вычитается от стандартного времени, которое основано на атомных часах, для поддержания соответствия с вращением Земли. При необходимости корректировки сообщается заранее, а сама корректировка выполняется в конце последнего дня месяца, в котором было уведомление, как правило в июне или декабре.В первой минуте на следующий день будет либо 59 или 61 сек.0 - нет предупреждения
1 - +1 сек (в минуте будет 61 сек)
2 - -1 сек (в минуте будет 59 сек)
3 - часы не синхронизированы
Version number
Номер версии NTP протокола. 3 - cейчас используется 3-ий.Mode
Определяет тип пакета:0 - зарезервировано
1 - symmetric active
2 - client
3 - server
4 - broadcast
5 - NTP control message
Peer Clock Stratum
Определяет уровень (Stratum) сервера:0 - не указан
1 - первичный, сам содержит эталон
2-255 - вторичные
Peer Polling Interval
Период между опросами. Представляет собой целое число со знаком. Период указывается в секундах как степень числа 2. Например Peer Polling Interval = 10, это означает, что период равен 2^10 = 1024 сек. Показывает, насколько часто клиент синхронизируется с сервером. Зависит от точности тактового генератора клиента. Сервер возвращает это значение обратно.Peer Clock Precision
Показывает точность часов сервера. Т.е. максимальную разность между стандартным временем и часами сервера. Формат такой же как Peer Polling Interval.Root Delay
Время отклика. Полное время отклика от вышестоящего сервера. Знаковое 16-ти битное число с фиксированной запятой. Запятая расположена между битами 15 и 16. Т.е. показывает время в 1/(2^16) сек.Root Dispersion
Дисперсия времени отклика. Формат такой же как и Root Delay.Reference ID
Идентификатор опорного источника тактового сигнала. Содержимое зависит от Peer Clock Stratum, если первичный - это текстовая строка с названием генератора, если вторичный - это IP-адрес вышестояшего сервера.Различные отметки времени
Состоят из двух 32-х разрядных чисел. Первое представляет собой целую часть количества секунд прошедших с 1 января 1900 г. по UTC. Второе - дробную часть 1/2^32.Reference Timestamp
Отметка времени, когда в последний раз происходила синхронизация времени с вышестоящим сервером.Origin Timestamp
Отметка времени, полученная от клиента. Снова ему возвращается.Receive Timestamp
Отметка времени, когда сервер получил запрос.Transmit Timestamp
Отметка времени по часам клиента, когда он отправил запрос или отметка времени по часам сервера, когда он отправил ответ.
В более полном варианте клиент запоминает момент времени запроса по своим часам и проставляет его в Transmit Timestamp. Получив ответ, клиент сравнивает эту отметку с Origin Timestamp - они должны быть равны. Этим обеспечивается защита от случайного получения пакета не на свой запрос. Также, разделив время отклика от сервера на 2, можно узнать приблизительное время прохождения пакета и вычесть его из Transmit Timestamp.
Практическое занятие.
Перед тем, как приступить к написанию функций на встроенном устройстве, был написал скрипт на PHP, чтобы полностью разобратся с получением времени через Интернет.Сервер: , протокол:
Скачать скрипт NTP_backend