Верх страницы
Обложка к записи Синхронизация времени с NTP — сервером на PHP
Время для прочтения: 0 мин. 56 сек.

Синхронизация времени с 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

Ссылки

Автор: Кобзарёв Михаил

Русский разработчик с 20-ти летним стажем. Работаю с PHP, ООП, JavaScript, Git, WordPress, Битрикс, Joomla, Drupal, OpenCart, DLE, Laravel, Moonshine, Symfony, SuiteCRM.

Оптимизирую сайты под Google Page Speed, настраиваю импорты для больших магазинов на WooCommerce + WP All Import. Пишу плагины на заказ. Все мои услуги.

Веду блог о разработке, дайджест в телеграмме и в ВК.

Вы всегда можете нанять меня.

Комментарии
Подписаться
Уведомить о
guest

12 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
Андрей
13 лет назад

Отлично написано! Лучшая статья на эту тему, которую я нашел.

Последний раз редактировалось 1 месяц назад Кобзарёв Михаил ем
Andrey
Andrey
13 лет назад

—NTP использует для своей работы протокол UDP.—

Ну и где в вашем примере UDP? И разве NTP работает не на 123 udp порту?

Andrey
Andrey
13 лет назад

Ну так вот и не вводите людей в заблуждение.
Цитата из wiki
—NTP не следует путать с daytime protocol RFC 867 или time protocol RFC 868—

А то я уже обрадовался, думал и правда нашёл реализацию NTP на PHP 🙂

rsgrinko
rsgrinko
2 месяцев назад
Ответить на  Andrey

вот пример реализации именно NTP на пыхе

<?php
	function ntp(string $server,int $port = 123): ?string
	{
		$socket = @fsockopen('udp://' . $server, $port, $errNo, $errStr, 1);
		if (!$socket){
			return null;
		}
		fwrite($socket,chr(0x1b) . str_repeat("\0", 47));
		$packetReceived = fread($socket, 48);
		$unixTimestamp = unpack('N', $packetReceived, 40)[1] - 2208988800;
		return date('Y-m-d H:i:s', $unixTimestamp);
	}


	echo ntp('0.ru.pool.ntp.org');
Andrey
Andrey
13 лет назад

А причем тут stream_socket_server? Зачем открывать у себя порт? Ваша статья же не о том как на php сделать сервер времени. Fsockopen() может прекрасно работать с udp, дело не в этом. Вы вначале процитировали wiki, о том, что такое NTP, а в коде реализовали DATETIME, который c NTP ничего общего не имеет.

Andrey
Andrey
13 лет назад

Да нет, просто сменить порт и указать протокол, недостаточно. Этот самый алгоритм «Марзулло» нужно реализовывать в коде. В общем нужно копать RFC 🙂

Предыдущая запись

Давайте дружить
в Telegram

Авторский блог вашего покорного слуги в Telegram про web, программирование, алгоритмы, инструменты разработчика, WordPress, Joomla, Opencart, Laravel, Moonshine, фильмы и сериалы