Синхронизация времени с NTP — сервером на PHP
Одним из первых протоколов точного времени, используемым на компьютерах, был DAYTIME (RFC 867), предназначенный для сообщения даты и времени в понятном человеку виде.
Формат ответа DAYTIME строго не регламентируется и не предназначен для машинной обработки — предполагается лишь, что человеку, прочитавшему полученную строку, станет ясно текущее время.
Реализуем простую функцию для получения точного времени:
function queryTimeServer ($timeserver, $socket) {
$timevalue = 0;
$ret = array();
$fp = @fsockopen($timeserver,$socket,$err,$errstr,5);
if ($fp) {
fputs($fp,"\n");
$timevalue = fread($fp,49);
fclose($fp);
}
$ret['time'] = $timevalue;
$ret['errno'] = $err;
$ret['errstr'] = $errstr;
return $ret;
}
Используем, например, так:
// По умолчанию берем время с нашего сервера
$timevalue = time();
// Если запрос к NTP успешен - берем время из него
$timercvd = queryTimeServer("pool.ntp.org", 13);
if (!$timercvd['errno'] && $timercvd['time'] > 0) {
$timevalue = strtotime($timercvd[0]);
}
echo "<p>Точное время: {$timevalue}</p>";
Для получения точного времени помимо pool.ntp.org
также можно использовать следующие сервера:
- time.windows.com
- time.nist.gov
- time-a.nist.gov
- time-b.nist.gov
- time-a.timefreq.bldrdoc.gov
- time-b.timefreq.bldrdoc.gov
- time-c.timefreq.bldrdoc.gov
Делаем то же самое, но по протоколу NTP:
<?php
function getNTPTime($server = 'pool.ntp.org') {
$ntpTime = 0;
$timeOffset = 2208988800; // Смещение времени с 1900 на 1970 год
$ntpPort = 123;
$ntpPacket = "\010" . str_repeat("\0", 47); // NTP запрос (LI=0, VN=4, Mode=3)
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) {
throw new Exception("Ошибка при создании сокета: " . socket_strerror(socket_last_error()));
}
socket_sendto($socket, $ntpPacket, strlen($ntpPacket), 0, $server, $ntpPort);
socket_recvfrom($socket, $response, 48, 0, $server, $ntpPort);
socket_close($socket);
// Извлекаем время из ответа
$unpacked = unpack('B*', substr($response, 40, 4));
$time = bindec(trim($unpacked[1]));
// Конвертируем в UNIX timestamp
$ntpTime = $time - $timeOffset;
return date('Y-m-d H:i:s', $ntpTime);
}
Пример использования:
<?php
try {
echo "Текущее время с NTP сервера: " . getNTPTime();
} catch (Exception $e) {
echo "Ошибка: " . $e->getMessage();
}
Не следует путать daytime protocol RFC 867 с NTP или time protocol RFC 868
Отлично написано! Лучшая статья на эту тему, которую я нашел.
Так это тоже самое 🙂
—NTP использует для своей работы протокол UDP.—
Ну и где в вашем примере UDP? И разве NTP работает не на 123 udp порту?
Порт 13/TCP, UDP: протокол DAYTIME — предназначен для тестирования связи путём получения от сервера текущих даты и времени в текстовом виде (Ссылка в статье). Можно использовать 37 порт, но он устарел.
Ну так вот и не вводите людей в заблуждение.
Цитата из wiki
—NTP не следует путать с daytime protocol RFC 867 или time protocol RFC 868—
А то я уже обрадовался, думал и правда нашёл реализацию NTP на PHP 🙂
Что мешает сделать, например, так (подробности смотрите тут):
вот пример реализации именно NTP на пыхе
Обновил пост более развернутым примером.
А причем тут stream_socket_server? Зачем открывать у себя порт? Ваша статья же не о том как на php сделать сервер времени. Fsockopen() может прекрасно работать с udp, дело не в этом. Вы вначале процитировали wiki, о том, что такое NTP, а в коде реализовали DATETIME, который c NTP ничего общего не имеет.
Значит, мы друг друга не правильно поняли. Главное, чтобы серверный фаервол вас пропустил 🙂
Да нет, просто сменить порт и указать протокол, недостаточно. Этот самый алгоритм «Марзулло» нужно реализовывать в коде. В общем нужно копать RFC 🙂
Буду рад помочь. Статью я поправил. Сам того не замечая, писал про одно, а реализовал другое.