Requête envoyée:
00011011 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
11100110 10100001 11100000 00001100
00000000 00000000 00000000 00000000

Réponse reçue:
00011100 00000010 00000011 11101000
00000000 00000000 00000001 00101100
00000000 00000000 00000001 01100110
10010001 11101110 11001011 00001110
11100110 10100001 11011111 01011110
11011110 00100101 00100011 01011010
11100110 10100001 11100000 00001100
00000000 00000000 00000000 00000000
11100110 10100001 11100000 00001100
11110000 00011001 11010110 11010011
11100110 10100001 11100000 00001100
11110000 00011010 10111100 11111100

Leap Indicatorrien
Version Number3
Modeserveur
Strate2

Reference Clock Update16603793582022-08-13T10:29:18+02:00
Originate Time Stamp16603795322022-08-13T10:32:12+02:00
Receive Time Stamp16603795322022-08-13T10:32:12+02:00
Transmit Time Stamp16603795322022-08-13T10:32:12+02:00

Délai Aller Retour0 sec.
Ecart0 sec.
Date Locale2022-08-13T10:32:12+02:00
Date Corrigée2022-08-13T10:32:12+02:00

<?php

/**
 * @file client_ntp.php
 * @brief Client NTP en PHP
 *
 * @author hughes monget
 * @see http://www.monget.com/
 * @since 2012-02-14
 * @date 2012-02-14
 */

###### PARAMETRES PHP ######

error_reporting(E_ALL E_NOTICE E_STRICT);
set_time_limit(0);
$arr_ini_set = array('log_errors' => 0'display_errors' => 1'error_log' => 0'html_errors' => 0'date.timezone' => 'Europe/Paris');
array_walk($arr_ini_setcreate_function('$v, $k''ini_set($k, $v);'));
if (!
setlocale(LC_ALL'french')) { echo 'locale not set'; }


###### CONSTANTES ######

// Calcul du nombre de secondes de 1900 jusqu'en 1970.
$int_nombre_seconde_1900_1970 0.0;
foreach (
range(19001969) as $int_annee)
{
    
$int_nombre_jour est_bissextile($int_annee) ? 366.0 365.0;
    
$int_nombre_seconde_1900_1970 += $int_nombre_jour 24.0 60.0 60.0;
}

define('NOMBRE_SECONDE_1900_1970'$int_nombre_seconde_1900_1970);
//define('NOMBRE_SECONDE_1900_1970', 2208988800.0);

define('LONGEUR_MESSAGE_EN_OCTET'48);

// http://www.pool.ntp.org/zone/europe
define('NOM_SERVEUR_NTP''fr.pool.ntp.org');

define('PORT_SERVEUR_NTP'123);


###### FONCTIONS ######

/**
 * @param $int_annee - integer - une année
 * @return boolean - TRUE si l'année est bisextile
 */
function est_bissextile($int_annee)
{
    if (
$int_annee 400 == 0)     { return TRUE; }
    elseif (
$int_annee 100 == 0) { return FALSE; }
    elseif (
$int_annee == 0)   { return TRUE;    }
    else { return 
FALSE; }
}

/**
 * Cette fonction transforme 4 octets d'un message NTP en un timestamp unix.
 * @param $arr_raw - array of octet
 * @param $int_position - integer - la position de début du mot de 32 bits.
 * @return integer - un timestamp unix.
 */
function extraire_seconde($arr_raw$int_position)
{
    
$mix_return $arr_raw[$int_position].$arr_raw[$int_position+1].$arr_raw[$int_position+2].$arr_raw[$int_position+3];
    
$mix_return unpack('N'$mix_return);
    
$mix_return reset($mix_return);
    
$mix_return floatval($mix_return);
    if (
$mix_return 0)
    {
        
$mix_return += pow(232);
    }
    if (
$mix_return NOMBRE_SECONDE_1900_1970)
    {
        
$mix_return -= NOMBRE_SECONDE_1900_1970;
    }
    return 
intval($mix_return);
}


/**
 * Cette fonction fait afficher un tableau d'octet sous sa forme binaire.
 * @param $arr_octect - array of octet
 */
function afficher_message($arr_octect)
{
    for (
$ii 0$nn count($arr_octect); $ii $nn$ii++)
    {
        
$octet $arr_octect[$ii];
        
printf('%08b 'ord($octet));
        if ((
$ii 1) % == 0)
        {
            echo 
'<br />';
        }
    }
}


###### MAIN ######

function main()
{
    
// On envoie le début du html.
    
echo
    
'
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
        <title>Client NTP</title>
    <style type="text/css">
    <!--
    * { font: 14px monospace; }
    table { border-collapse: collapse; }
    table, td, th { border: 1px solid #000; }
    td, th { padding: 5px; }
    -->
    </style>
    </head>
    <body>
    '
;

    
// On prépare la requête à envoyer au serveur NTP.
    
$arr_octect array_fill(0LONGEUR_MESSAGE_EN_OCTET"\x0");

    
// 1 octect => indicateur de saut  Version du protocole  Mode Client
    //             00                  011                   011
    // 00 011 011 ==  0001 1011 == 0x1B
    
$arr_octect[0] = "\x1B";

    
// On remplit le Transmit Timestamp, il nous reviendra
    // dans l'Originate Timestamp.
    
$mix_originate floatval(time()) + NOMBRE_SECONDE_1900_1970;
    
$mix_originate pack('N'$mix_originate);

    
$arr_octect[40] = $mix_originate[0];
    
$arr_octect[41] = $mix_originate[1];
    
$arr_octect[42] = $mix_originate[2];
    
$arr_octect[43] = $mix_originate[3];

    
/*
    // On remplit les secondes de valeurs aléatoires.
    foreach (range(44, 47) as $int_position)
    {
        $arr_octect[$int_position] = chr(mt_rand(0, 0xFF));
    }
    */

    // On affiche le message que l'on envoie.
    
echo '<hr />Requête envoyée: <br />';

    
afficher_message($arr_octect);

    
// On envoie la requête au serveur NTP.
    
$raw_octect implode($arr_octect);

    
// On ouvre la socket.
    
ini_set('default_socket_timeout'3);

    
$int_t1a time();
    
$res_socket fsockopen('udp://'.NOM_SERVEUR_NTPPORT_SERVEUR_NTP);

    if (
$res_socket === FALSE)
        { exit(
'Problème de connexion au serveur NTP'); }

    
// On envoie la requête.
    
if (fwrite($res_socket$raw_octect) === FALSE)
        { exit(
'Problème d\'envoi de données'); }

    
// On récupère la réponse.
    
$raw_octect fread($res_socketLONGEUR_MESSAGE_EN_OCTET);
    
$int_t2a time();
    if (
$arr_octect === FALSE)
        { exit(
'Problème de lecture de données'); }

    
fclose($res_socket);

    
// On contrôle la réponse.
    
$arr_octect str_split($raw_octect);
    if (
count($arr_octect) != LONGEUR_MESSAGE_EN_OCTET)
        { exit(
'Problème de taille de message'); }

    
// On affiche le message reçu en binaire.
    
echo '<hr />Réponse reçue: <br />';
    
afficher_message($arr_octect);

    
// On affiche les différentes informations issues du serveur.
        // Saut de seconde
    
$arr_str_saut = array(=> 'rien'=> 'ajouter une seconde'=> 'enlever une seconde'=> 'horloge non synchronisée');
    
$int_saut = (ord($arr_octect[0]) >> 6) & 0x3;
    
$str_saut = isset($arr_str_saut[$int_saut]) ? $arr_str_saut[$int_saut] : '-';
        
// Version
    
$int_version = (ord($arr_octect[0]) >> 3) & 0x7;
        
// Mode
    
$int_mode ord($arr_octect[0]) & 0x7;
    
$arr_str_mode = array(=> 'symétrique actif'=> 'symétrique passif'=> 'client'=> 'serveur'=> 'broadcast '=> 'message de contrôle NTP');
    
$str_mode = isset($arr_str_mode[$int_mode]) ? $arr_str_mode[$int_mode] : '-';
        
// ID Serveur: sauf erreur de ma part, il n'envoie pas l'IP du serveur comme annoncé.
    //$str_identifier = strval(ord($arr_octect[12])).'.'.strval(ord($arr_octect[13])).'.'.strval(ord($arr_octect[14])).'.'.strval(ord($arr_octect[15]));
    //$str_identifier .= ' - '.$arr_octect[12].$arr_octect[13].$arr_octect[14].$arr_octect[15];
        // Strate
    
$int_strate ord($arr_octect[1]);

    echo 
'<hr />';
    echo 
'<table>';
    echo 
"<tr><td>Leap Indicator</td><td>$str_saut</td></tr>";
    echo 
"<tr><td>Version Number</td><td>$int_version</td></tr>";
    echo 
"<tr><td>Mode</td><td>$str_mode</td></tr>";
    
//echo "<tr><td>Reference Clock Identifier</td><td>$str_identifier</td></tr>";
    
echo "<tr><td>Strate</td><td>$int_strate</td></tr>";
    echo 
'</table>';

    
// On affiche les différents temps reçus.
    
echo '<hr />';
    echo 
'<table>';
    foreach (array(
'Reference Clock Update' => 16'Originate Time Stamp' => 24'Receive Time Stamp' => 32'Transmit Time Stamp' => 40) as $str_libelle => $int_position)
    {
        
$int_date extraire_seconde($arr_octect$int_position);
        
$str_date date('c'$int_date);
        echo 
"<tr><td>$str_libelle</td><td>$int_date</td><td>$str_date</td></tr>";
    }
    echo 
'</table>';

    
// On affiche les différentes valeurs calculées.
    
echo '<hr />';

    
$int_t1b extraire_seconde($arr_octect32);
    
$int_t2b extraire_seconde($arr_octect40);

    
$int_delai_aller_retour = ($int_t2a $int_t1a) - ($int_t2b $int_t1b);
    
$int_ecart intval(($int_t1b $int_t2b) / 2.0 - ($int_t1a $int_t2a) / 2.0);
    
$int_date_locale time();
    
$str_date_locale date('c'$int_date_locale);
    
$int_date_corrigee $int_date_locale $int_ecart;
    
$str_date_corrigee date('c'$int_date_corrigee);

    
$str_info = ($int_ecart) ? (($int_ecart 0) ? '(en retard)' '(en avance)') : '';

    echo 
'<table cellpadding="2x" border="1px">';
    echo 
"<tr><td>Délai Aller Retour</td><td>$int_delai_aller_retour sec.</td></tr>";
    echo 
"<tr><td>Ecart</td><td>$int_ecart sec. $str_info</td></tr>";
    echo 
"<tr><td>Date Locale</td><td>$str_date_locale</td></tr>";
    echo 
"<tr><td>Date Corrigée</td><td>$str_date_corrigee</td></tr>";
    echo 
'</table>';

    echo 
'<hr />';

    
highlight_file(__FILE__);

    
// Fin html
    
echo '</body></html>';
}

main();