Columna: Diseño de un cliente de servicio horario NTP desde cero: introducción al protocolo NTP y programación UDP práctica

Introducción al NTP

Excepto por la parte del código de este artículo, la saqué de los artículos de otras personas. Solo explica mis ideas de lectura personales. Si los lectores ven algún escrito incorrecto, perdónenme. Puede saltar al texto original al final del artículo para verlo. !

Descripción general

Protocolo de tiempo de red, nombre en inglés: Network Time Protocol (NTP) es un protocolo utilizado para sincronizar la hora de la computadora y permite que la computadora sincronice su servidor o fuente de reloj (como reloj de cuarzo, GPS, etc.).

Puede proporcionar una corrección de tiempo de alta precisión (la diferencia con el estándar es menos de 1 milisegundo en LAN y decenas de milisegundos en WAN) y puede prevenir ataques de protocolo viciosos mediante confirmación cifrada. El propósito de NTP es proporcionar servicios horarios precisos y sólidos en un entorno desordenado de Internet.

NTP transmite basándose en paquetes UDP y el número de puerto UDP utilizado es 123.

El propósito de usar NTP es sincronizar los relojes de todos los dispositivos con los relojes de la red para que los relojes de todos los dispositivos en la red sean consistentes, de modo que los dispositivos puedan proporcionar una variedad de aplicaciones basadas en el tiempo unificado.

Para un sistema local que ejecuta NTP, no solo puede recibir sincronización de otras fuentes de reloj, sino que también sirve como fuente de reloj para sincronizar otros relojes y puede sincronizarse con otros dispositivos.

principio de funcionamiento

Método para realizar

Reloj inalámbrico: el sistema del servidor puede conectarse a un reloj inalámbrico a través del puerto serie. El reloj inalámbrico recibe señales transmitidas por satélites GPS para determinar la hora actual. El reloj inalámbrico es una fuente de tiempo muy precisa, pero requiere una cierta tarifa.

Servidor de hora: También puede utilizar el servidor de hora NTP de la red para sincronizar los relojes de los sistemas de la red a través de este servidor.

Sincronización dentro de la red de área local: si solo necesita sincronizar relojes entre sistemas dentro de esta red de área local, puede usar el reloj de cualquier sistema en la red de área local. Debe seleccionar el reloj de un nodo en la red de área local. como fuente de tiempo "autorizada", entonces otros nodos solo necesitan sincronizar la hora con esta fuente de tiempo. Con este método,

Todos los nodos utilizarán un reloj de sistema común, pero no es necesario sincronizar el reloj con sistemas fuera de la LAN. Si un sistema está dentro de una LAN y no se puede utilizar un reloj inalámbrico, este método es la mejor opción.

Proceso de trabajo

En este momento, solo enumero el modo de trabajo [Proceso de interacción entre el servidor NTP y el cliente]. Hay muchos otros modos de trabajo. Consulte este artículo. Las cosas escritas son muy detalladas. Básicamente las transfiero desde aquí. Vine aquí.

Análisis simple del protocolo NTP.

El proceso de interacción entre el servidor NTP y el cliente.

Tanto el cliente como el servidor tienen una línea de tiempo, que representa la hora de sus respectivos sistemas. Cuando el cliente desea sincronizar la hora del servidor, el cliente construirá un paquete de protocolo NTP y lo enviará al servidor NTP, y el cliente registrará esto. Se envía en el momento t0. Después de un período de transmisión de retardo de la red, el servidor recibe el paquete de datos en el momento t1 y lo devuelve al cliente en el momento t2 después de un período de procesamiento.

Después de un período de transmisión retrasada de la red, el cliente recibe el paquete de datos del servidor NTP en el momento t3.

t0 y t3 son las horas del sistema horario del cliente y t1 y t2 son las horas del sistema horario del servidor NTP, son diferentes. t0, t1 y t2 corresponden respectivamente a los tres parámetros en el mensaje server->cient NTP:

t0: marca de tiempo de origen (la hora en que el cliente envía el paquete de datos)

t1: recibir marca de tiempo (la hora en que el servidor recibió el paquete de datos)

t2: marca de tiempo de transmisión (el servidor devuelve la hora del paquete)

t3: marca de tiempo de recepción del cliente (hora local en la que el cliente recibe el mensaje de respuesta).

Cálculo de retraso y desviación de tiempo.

Supuesto: La desviación del sistema de tiempo entre el cliente y el servidor se define como θ , y el retraso de ida y vuelta de la red (retraso unidireccional) se define como δ .

El proceso de derivación:

  1. Según el principio de interacción, se puede enumerar un sistema de ecuaciones:

t0+θ+δ=t1
t2-θ+δ=t3 // 这里我也还没理解为啥是这样的? 如果看到这里的同学可以思考一下
  1. Resolviendo el sistema de ecuaciones obtenemos los siguientes resultados:

θ=(t1-t0+t2-t3)/2  
δ=(t1-t0+t3-t2)/2 

El método de límite se puede utilizar al memorizar, suponiendo que el retraso y la desviación son 0 respectivamente.

Calibración de tiempo del cliente

Para dispositivos con requisitos de tiempo menos precisos, el cliente puede solidificar el tiempo de retorno del servidor t2 como hora local.

Sin embargo, como protocolo de comunicación estándar, se debe calcular el retraso de transmisión de la red y t2+δ debe solidificarse como hora local.

El algoritmo de calibración de tiempo del cliente anterior es sólo para comprender el proceso y no representa la práctica real.

mensaje NTP

Ejemplo de mensaje NTP

Entre ellos, 192.10.10.189 es el lado del servidor de NTP y 192.10.10.32 es el lado del cliente.

formato de mensaje NTP
  • NTP tiene dos tipos diferentes de mensajes, uno es el mensaje de sincronización del reloj y el otro es el mensaje de control.

  • Los mensajes de control sólo se utilizan cuando se requiere gestión de red, no son necesarios para la función de sincronización del reloj y no serán analizados por el momento.

  • El formato del mensaje de sincronización del reloj es el siguiente:

El formato del mensaje NTP se muestra en la figura anterior y el significado de sus campos es el siguiente:

  • LI (Leap Indicator): Identificador de segundo intercalar, con una longitud de 2 Bits, utilizado para avisar de la inserción o eliminación de 1s en el último minuto.

Implementación del código en lenguaje C.

#include <stdio.h>
#include "ntp.h"

int main (void) 
{
    const char * server_list[] = {
        "windows.com",    
        "time.windows.com",
    };

    for (int i = 0; i < sizeof(server_list) / sizeof(server_list[0]); i++) {
        struct tm * t = ntp_get_time(server_list[i]);
        if (t) {
            printf("current time is : %s\n", asctime(t));
            return 0;
        }
    }
    printf("ntp: get time failed, stop\n");
    return -1;
}
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include "ntp.h"

struct tm * ntp_get_time (const char * server) 
{
    printf("ntp: try to get time from %s\n", server);

    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sockfd < 0) {
        printf("ntp: create socket error\n");
        return NULL;
    }

    struct hostent * ent = gethostbyname(server);
    if (!ent) {
        printf("resolve host name failed.");
        goto failed;
    }

    char str[INET_ADDRSTRLEN];
    printf("server ip is: %s\n", inet_ntop(ent->h_addrtype, ent->h_addr, str, sizeof(str)));

    struct timeval tmo;
    tmo.tv_sec = NTP_REQ_TMO;
    tmo.tv_usec = 0;
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tmo, sizeof(tmo));

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(NTP_SERVER_PORT);
    addr.sin_addr = *(struct in_addr *)ent->h_addr;
    if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        printf("ntp: connect to ntp server failed.");
        goto failed;
    }

    for (int i = 0; i < NTP_REQ_RETRY; i++) {
        ntp_pkt_t pkt;
        memset(&pkt, 0, sizeof(pkt));
        pkt.LI_VN_Mode = (NTP_MODE << 0) | (NTP_VERSION << 3);

        if (send(sockfd, (const void *)&pkt, sizeof(ntp_pkt_t), 0) < 0) {
            printf("ntp: send to ntp failed.\n");
            break;
        }

        memset(&pkt, 0, sizeof(pkt));
        if (recv(sockfd, (void *)&pkt, sizeof(ntp_pkt_t), 0) < 0) {
            printf("ntp: recv failed.\n");
            continue;
        }

        pkt.trans_ts.seconds = ntohl(pkt.trans_ts.seconds);

        // 1970.1.1. - 1900.1.1
        time_t t = pkt.trans_ts.seconds - NTP_TIME_1900_1970;
        close(sockfd);
        return localtime(&t);
    }
failed:
    close(sockfd);
    return NULL;
}
#ifndef NTP_H
#define NTP_H

#include <time.h>
#include <stdint.h>

#define NTP_TIME_1900_1970  2208988800
#define NTP_SERVER_PORT     123
#define NTP_VERSION         3
#define NTP_MODE            3

#define NTP_REQ_TMO         3
#define NTP_REQ_RETRY       3


#pragma pack(1)
// 1000.2 s
// 1900.1.1 0:00:00  s
typedef struct _ntp_ts_t 
{
    uint32_t seconds;
    uint32_t fraction;
}ntp_ts_t;

typedef struct _ntp_pkt_t {
    uint8_t LI_VN_Mode;
    uint8_t stratum;
    uint8_t poll;
    uint8_t precision;
    uint32_t root_delay;
    uint32_t root_dispersion;
    uint32_t ref_id;
    ntp_ts_t ref_ts;
    ntp_ts_t orig_ts;
    ntp_ts_t recv_ts;
    ntp_ts_t trans_ts;
}ntp_pkt_t;

#pragma pack()

struct tm * ntp_get_time (const char * server);

#endif

Fuente del artículo: Introducción a NTP

Supongo que te gusta

Origin blog.csdn.net/buhuidage/article/details/129787309
Recomendado
Clasificación