专栏从0设计NTP时间服务客户端-NTP协议介绍和UDP编程实战

news/2024/5/18 14:09:15 标签: udp, 网络, 网络协议, tcp/ip, linux

NTP入门介绍

这篇文章除了代码部分,其他均为我从其他人文章处搬运过来的,只阐述我个人的阅读思路,读者如果看到写的不好的地方敬请谅解,可以从文章底部原文跳转查看!

概述

网络时间协议,英文名称:Network Time Protocol(NTP) 是用来使计算机时间同步化的一种协议,它可以使计算机对其服务器或时钟源(如石英钟,GPS等等)做同步化,

它可以提供高精准度的时间校正(LAN上与标准间差小于1毫秒,WAN上几十毫秒), 且可介由加密确认的方式来防止恶毒的协议攻击。NTP的目的是在无序的Internet环境中提供精确和健壮的时间服务。

NTP 基于 UDP 报文进行传输, 使用的UDP端口号为 123.

使用 NTP 的目的是对网络内所有具有时钟的设备进行时钟同步, 使网络内所有设备的时钟保持一致, 从而使设备能够提供基于统一时间的多种应用.

对于运行 NTP 的本地系统, 既可以接收来自其他时钟源的同步, 又可以作为时钟源同步其他的时钟, 并且可以和其他设备相互同步.

工作原理

实现方式

无线时钟: 服务器系统可以通过串口连接一个无线时钟. 无线时钟接收 GPS 的卫星发射的信号来决定当前时间. 无线时钟是一个非常精确的时间源, 但是需要花一定的费用.

时间服务器: 还可以使用网络中 NTP 时间服务器, 通过这个服务器来同步网络中的系统的时钟.

域网内的同步: 如果只是需要在本局域网内进行系统间的时钟同步, 那么就可以使用局域网中任何一个系统的时钟. 你需要选择局域网中的一个节点的时钟作”权威的”的时间源, 然后其它的节点就只需要与这个时间源进行时间同步即可. 使用这种方式,

所有的节点都会使用一个公共的系统时钟, 但是不需要和局域网外的系统进行时钟同步. 如果一个系统在一个局域网的内部, 同时又不能使用无线时钟, 这种方式是最好的选择.

工作流程

这里目前我只列举【NTP服务端与客户端的交互过程】这一工作模式,还有许多其他的工作模式, 请参考这边文章,东西写的是真的详细,我的基本都是从这里搬运过来的.

NTP 协议简单分析

NTP服务端与客户端的交互过程

客户端和服务端都有一个时间轴,分别代表着各自系统的时间,当客户端想要同步服务端的时间时,客户端会构造一个NTP协议包发送到NTP服务端,客户端会记下此时发送的时间t0,经过一段网络延时传输后,服务器在t1时刻收到数据包,经过一段时间处理后在t2时刻向客户端返回数据包,

再经过一段网络延时传输后客户端在t3时刻收到NTP服务器数据包。

t0和t3是客户端时间系统的时间、t1和t2是NTP服务端时间系统的时间,它们是有区别的。 t0、t1、t2分别对应着server->cient NTP报文中的三个参数:

t0:origin timestamp(客户端发送数据包的时间)

t1: receive timestamp(服务器接收到数据包的时间)

t2: transmit timestamp(服务器返回数据包时间)

t3: client receive timestamp(客户端收到回复报文时本地的时间)。

延时和时间偏差计算

假设:客户端与服务端的时间系统的偏差定义为θ网络的往/返延迟(单程延时)定义为δ

推导过程:

  1. 根据交互原理,可以列出方程组:

t0+θ+δ=t1
t2-θ+δ=t3 // 这里我也还没理解为啥是这样的? 如果看到这里的同学可以思考一下
  1. 求解方程组,得到以下结果:

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

记忆时可以采用极限法,分别假设延时和偏差为0.

客户端时间校准

对于时间要求不那么精准设备,客户端可把服务端端的返回时间t2固化为本地时间。

但是作为一个标准的通信协议,必须计算上网络的传输延时,需要把t2+δ固化为本地时间。

以上client时间校准算法只为理解过程,不代表真实做法

NTP报文

NTP报文示例

其中192.10.10.189为NTP的server端,192.10.10.32为client端。

NTP报文格式

  • NTP 有两种不同类型的报文, 一种是 时钟同步报文, 一种是 控制报文.

  • 控制报文 仅用于需要网络管理的场合, 对于时钟同步功能不是必需的, 暂不做分析.

  • 时钟同步报文格式如下:

NTP报文格式如上图所示,它的字段含义参考如下:

  • LI (Leap Indicator): 闰秒标识器, 长度为 2 Bits, 用来预警最近一分钟插入 1s 或者删除 1s.

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

文章来源: NTP入门介绍


http://www.niftyadmin.cn/n/187596.html

相关文章

java基础知识——13.类与对象

这篇文章&#xff0c;我们来介绍java中的类与对象 目录 1.面向对象的介绍 2.类的设计与使用 2.1 类和对象 2.1.1 如何定义类 2.2 类的注意事项 3.封装 3.1 private关键字 4.this关键字 5.构造方法 6.标准JavaBean 7.对象内存图 8.成员变量与局部变量 1.面向对象的…

iOS_从相机或相册里扫描二维码或条形码

文章目录1. 从相机里扫描1.1 申请相机权限1.2 创建Scanner1.3 开始扫描1.4 处理扫描结果2. 从相册里扫描2.1 获取相册权限2.2 打开相册2.3 获得选择结果2.4 解析相片中的二维码或条形码1. 从相机里扫描 1.1 申请相机权限 导入&#xff1a; import AVFoundation在项目的 Info.…

LC-1638. 统计只差一个字符的子串数目(中心扩散法)

1638. 统计只差一个字符的子串数目 难度中等62 给你两个字符串 s 和 t &#xff0c;请你找出 s 中的非空子串的数目&#xff0c;这些子串满足替换 一个不同字符 以后&#xff0c;是 t 串的子串。换言之&#xff0c;请你找到 s 和 t 串中 恰好 只有一个字符不同的子字符串对的…

Vue.js事件修饰符及v-model双向数据绑定

目录 一、事件修饰符 &#xff08;1&#xff09;.stop阻止事件冒泡 &#xff08;2&#xff09;.prevent阻止默认事件 &#xff08;3&#xff09;.capture事件捕获模式 &#xff08;4&#xff09;.self自身触发事件 &#xff08;5&#xff09;.once事件只触发一次 二、v-…

vs2010下 转换到 COFF 期间失败: 文件无效或损坏

因为同一个电脑上安装多个VS&#xff0c;有多个cvtres.exe。按照下面的操作如果还是不行就在C盘搜索cvtres.exe&#xff0c;然后挨个重命名&#xff0c;看看是调用的哪个&#xff0c;然后修改就可以了。 用VS2010编译C项目时出现这样的错误&#xff1a; LNK1123: 转换到 COFF …

Linux 系统安装 Pytorch

文章目录配置 Anaconda下载 Anaconda安装 Anaconda配置 Pytorch创建虚环境安装 Pytorch配置 Anaconda 下载 Anaconda &#xff08;1&#xff09;网页方式下载离线包 进入 Anaconda 官网 &#xff0c;出现如下的页面。 Anaconda 会根据访问网页所使用的系统&#xff0c;推荐对…

关于 Transformer 的面试题

面试题 请解释一下Transformer模型的注意力机制是如何工作的。 请解释一下Transformer模型中的编码器和解码器的作用是什么。 请简要解释一下Transformer模型与传统的循环神经网络模型的区别。 Transformer模型是否适用于非序列数据的任务&#xff1f;请举一个例子。 请解释…

一起来学5G终端射频标准(TAE for UL-MIMO)

01—TAE的定义我们先来了解一下TAE测试标准的发展演变。在4G LTE的3GPP 36.101-1的技术要求规范中&#xff0c;就给出了对4G终端UL MIMO以及V2X UE的TAE的定义和最小要求&#xff0c;但在36.521-1的4G终端一致性测试规范中并没有对应的章节规定TAE的一致性测试。5G中有所变化&a…