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
11100100 00100110 00001110 10001100
00000000 00000000 00000000 00000000

Réponse reçue:
00011100 00000010 00000001 11101000
00000000 00000000 00000001 00101100
00000000 00000000 00001001 00011000
11000001 10111110 11100110 01000010
11100100 00100110 00001011 01100100
00000001 11001000 01011101 01011111
11100100 00100110 00001110 10001100
00000000 00000000 00000000 00000000
11100100 00100110 00001110 10001100
11101001 11110101 01110100 00111001
11100100 00100110 00001110 10001100
11101001 11111011 01000111 01000111

Leap Indicatorrien
Version Number3
Modeserveur
Strate2

Reference Clock Update16187097322021-04-18T03:35:32+02:00
Originate Time Stamp16187105402021-04-18T03:49:00+02:00
Receive Time Stamp16187105402021-04-18T03:49:00+02:00
Transmit Time Stamp16187105402021-04-18T03:49:00+02:00

Délai Aller Retour0 sec.
Ecart0 sec.
Date Locale2021-04-18T03:49:00+02:00
Date Corrigée2021-04-18T03:49:00+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();