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
11101000 11001000 10010010 10101101
00000000 00000000 00000000 00000000

Réponse reçue:
00011100 00000010 00000011 11101000
00000000 00000000 00000001 00100011
00000000 00000000 00000010 01101000
10010001 11101110 11001011 00001110
11101000 11001000 10010000 01111011
11011110 00011101 10101110 11111001
11101000 11001000 10010010 10101101
00000000 00000000 00000000 00000000
11101000 11001000 10010010 10101101
01000111 00000100 11110111 11001110
11101000 11001000 10010010 10101101
01000111 00000101 11100111 00101001

Leap Indicatorrien
Version Number3
Modeserveur
Strate2

Reference Clock Update16964694992023-10-05T03:31:39+02:00
Originate Time Stamp16964700612023-10-05T03:41:01+02:00
Receive Time Stamp16964700612023-10-05T03:41:01+02:00
Transmit Time Stamp16964700612023-10-05T03:41:01+02:00

Délai Aller Retour0 sec.
Ecart0 sec.
Date Locale2023-10-05T03:41:01+02:00
Date Corrigée2023-10-05T03:41:01+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();