NTP协议详解

article/2025/10/6 19:09:54

前言
NTP(Network Time Protocol)网络时间协议基于UDP,用于网络时间同步的协议,使网络中的计算机时钟同步到UTC,再配合各个时区的偏移调整就能实现精准同步对时功能。提供NTP对时的服务器有很多,比如微软的NTP对时服务器,利用NTP服务器提供的对时功能,可以使我们的设备时钟系统能够正确运行。

NTP报文格式


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

 

LI 闰秒标识器,占用2个bit
VN 版本号,占用3个bits,表示NTP的版本号,现在为3
Mode 模式,占用3个bits,表示模式
stratum(层),占用8个bits
Poll 测试间隔,占用8个bits,表示连续信息之间的最大间隔
Precision 精度,占用8个bits,,表示本地时钟精度
Root Delay根时延,占用8个bits,表示在主参考源之间往返的总共时延
Root Dispersion根离散,占用8个bits,表示在主参考源有关的名义错误
Reference Identifier参考时钟标识符,占用8个bits,用来标识特殊的参考源    
参考时间戳,64bits时间戳,本地时钟被修改的最新时间。
原始时间戳,客户端发送的时间,64bits。
接受时间戳,服务端接受到的时间,64bits。
传送时间戳,服务端送出应答的时间,64bits。
认证符(可选项)


抛开复杂的协议报文,我们来理解一下NTP客户端与服务器的交互过程,进而理解参考时间戳、原始时间戳、接受时间戳、传送时间戳的关系。如图,客户端和服务端都有一个时间轴,分别代表着各自系统的时间,当客户端想要同步服务端的时间时,客户端会构造一个NTP协议包发送到NTP服务端,客户端会记下此时发送的时间t0,经过一段网络延时传输后,服务器在t1时刻收到数据包,经过一段时间处理后在t2时刻向客户端返回数据包,再经过一段网络延时传输后客户端在t3时刻收到NTP服务器数据包。特别声明,t0和t3是客户端时间系统的时间、t1和t2是NTP服务端时间系统的时间,它们是有区别的。对于时间要求不那么精准设备,直接使用NTP服务器返回t2时间也没有太大影响。但是作为一个标准的通信协议,它是精益求精且容不得过多误差的,于是必须计算上网络的传输延时。客户端与服务端的时间系统的偏移定义为θ、网络的往返延迟定义为δ,基于此,可以对t2进行精确的修正,已达到相关精度要求,它们的计算公式如下:

 

式中:

t0是请求数据包传输的客户端时间戳

t1是请求数据包回复的服务器时间戳

t2是响应数据包传输的服务器时间戳

t3是响应数据包回复的客户端时间戳

对此,我们只需将NTP服务端返回的时间t2加上网络延时δ的一半就可以了(t2+δ/2)。

NTP请求样例
#include <sys/types.h>  
#include <sys/stat.h> 
#include <sys/ioctl.h> 
#include <sys/socket.h>
#include <sys/wait.h> 
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> 
#include <stdio.h>  
#include <stdlib.h>
#include <string.h>  
#include <pthread.h>   
#include <dirent.h> 
#include <time.h>
#include <fcntl.h> 
#include <errno.h>
 
#define debugprintf 1
#ifdef debugprintf
    #define debugpri(mesg, args...) fprintf(stderr, "[NetRate print:%s:%d:] " mesg "\n", __FILE__, __LINE__, ##args) 
#else
    #define debugpri(mesg, args...)
#endif
 
#define JAN_1970             0x83aa7e80
#define NTPFRAC(x) (4294 * (x) + ((1981 * (x))>>11))
#define USEC(x) (((x) >> 12) - 759 * ((((x) >> 10) + 32768) >> 16))
#define Data(i) ntohl(((unsigned int *)data)[i])
#define LI 0
#define VN 3
#define MODE 3
#define STRATUM 0
#define POLL 4 
#define PREC -6
struct NtpTime 
{
    unsigned int coarse;
    unsigned int fine;
};
 
void sendPacket(int fd)
{
    unsigned int data[12];
    struct timeval now;
 
    if (sizeof(data) != 48) 
    {
        fprintf(stderr,"size error\n");
        return;
    }
 
    memset((char*)data, 0, sizeof(data));
    data[0] = htonl((LI << 30) | (VN << 27) | (MODE << 24) | (STRATUM << 16) | (POLL << 8) | (PREC & 0xff));//构造协议头部信息
    data[1] = htonl(1<<16);
    data[2] = htonl(1<<16);
    gettimeofday(&now, NULL);
    data[10] = htonl(now.tv_sec + JAN_1970);//构造传输时间戳
    data[11] = htonl(NTPFRAC(now.tv_usec));
    send(fd, data, 48, 0);
}
//获取NTP服务器返回的时间
void getNewTime(unsigned int *data,struct timeval *ptimeval)
{
    struct NtpTime trantime;
    trantime.coarse = Data(10);
    trantime.fine   = Data(11);
    
    ptimeval->tv_sec     = trantime.coarse - JAN_1970;
    ptimeval->tv_usec     = USEC(trantime.fine);
}
 
int getNtpTime(struct hostent* phost,struct timeval *ptimeval)
{
    if(phost == NULL)
    {
        debugpri("err:host is null!\n");
        return -1;
    }
    int sockfd;
    struct sockaddr_in addr_src,addr_dst;
    fd_set fds;
    int ret;
    int recv_len;
    unsigned int buf[12];
    memset(buf,0,sizeof(buf));
    int addr_len;
    int count = 0;
    
    struct timeval timeout;
 
    addr_len = sizeof(struct sockaddr_in);
 
    memset(&addr_src, 0, addr_len);
    addr_src.sin_family = AF_INET;
    addr_src.sin_addr.s_addr = htonl(INADDR_ANY);
    addr_src.sin_port = htons(0);
 
    memset(&addr_dst, 0, addr_len);
    addr_dst.sin_family = AF_INET;
    memcpy(&(addr_dst.sin_addr.s_addr), phost->h_addr_list[0], 4);
    addr_dst.sin_port = htons(123);//ntp默认端口123
 
    if(-1==(sockfd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)))//创建UDP socket
    {        
        debugpri("create socket error!\n");
        return -1;
    }
 
    ret = bind(sockfd, (struct sockaddr*)&addr_src, addr_len);//bind
    if(-1==ret)    
    {        
        debugpri("bind error!\n");        
        close(sockfd);        
        return -1;
    }
    
    ret = connect(sockfd, (struct sockaddr*)&addr_dst, addr_len);//连接NTP服务器
    if(-1==ret)    
    {        
        debugpri("connect error!\n");        
        close(sockfd);        
        return -1;
    }
    sendPacket(sockfd);    //发送请求包
    while (count < 50)//轮询请求
    {
        FD_ZERO(&fds);
        FD_SET(sockfd, &fds);
 
        timeout.tv_sec = 0;
        timeout.tv_usec = 100000;
        ret = select(sockfd + 1, &fds, NULL, NULL, &timeout);
        if (0 == ret)
        {
            count++;
            debugpri("ret == 0\n");
            sendPacket(sockfd);
            usleep(100*1000);
            continue;
        }
        if(FD_ISSET(sockfd, &fds))
        {
            recv_len = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&addr_dst, (socklen_t*)&addr_len);
            if(-1==recv_len)        
            {            
                debugpri("recvfrom error\n");            
                close(sockfd);            
                return -1;
            }
            else if(recv_len > 0)
            {
                debugpri("receiv data\n");
                getNewTime(buf,ptimeval);
                debugpri("sec = %d usec = %d",ptimeval->tv_sec ,ptimeval->tv_usec);//打印输出NTP服务器返回的时间
                break;
            }
        }
        else
        {
            debugpri("count %d \n",count);
            usleep(50*1000);
            count ++;
        }
    }
    if(count >=50)
    {
        debugpri("getNewTime   timeout fail \n");
        close(sockfd);
        return -1;
    }
    close(sockfd);
    return 0;
}
 
int main(int argc, char** argv)  
{
    struct timeval TimeSet;
    static struct hostent *host = NULL;
    
    host = gethostbyname(argv[1]);
    memset(&TimeSet ,0 ,sizeof(TimeSet));
    getNtpTime(host,&TimeSet);
    return 0;
     
 }
常用的NTP服务端站点
time.windows.com

time.nist.gov

s1a.time.edu.cn

s1c.time.edu.cn

time-nw.nist.gov

time-a.nist.gov

time-b.nist.gov

s1b.time.edu.cn

nist1.aol-ca.truetime.com

总结:
NTP协议作为常用的通信协议,各种参考资料齐全,本文做一次梳理,以加深对NTP协议的理解。参考:

https://zh.m.wikipedia.org/wiki/%E7%B6%B2%E8%B7%AF%E6%99%82%E9%96%93%E5%8D%94%E5%AE%9A

https://wenku.baidu.com/view/4ab65c3ec850ad02de80418e.html


http://chatgpt.dhexx.cn/article/u8aWh5qv.shtml

相关文章

NTP 协议

NTP: Network Time Protocal 一、定义&#xff1a;为实现高精确度的时间同步&#xff0c;而设计的网络时钟同步协议。在Linux系统中&#xff0c;它的最新实现是NTP 4.0&#xff08;一个分布式的网络时钟同步程序&#xff09;。相关定义和实现参看RFC1305和www.ntp.org。NTP协议…

NTP(Network Time Protocol)协议详解

一、NTP的基本概念&#xff1a; NTP(Network Time Protocol)------网络时间协议-----应用层协议&#xff0c;用来在分布式时间服务器和客户端之间进行时间同步。 二、采用NTP的目的&#xff1a; 是对网络内所有具有时钟的设备进行时钟同步&#xff0c;使网络内所有设备的时钟…

Windows10 debug安装、下载

目录 背景下载安装和使用 背景 由于Windows 10不支持debug命令&#xff0c;如果装虚拟机Windows XP就比较耗内存&#xff0c;所以我用的是DOSBox&#xff0c;然后挂载debug程序文件目录。 下载 DOSBox、debug、masm等等&#xff0c;我已经整理好了&#xff0c;下载地址&…

【WINDOWS系统】WinDbg调试工具

一、Windows 10 调试工具 (WinDbg)的安装 1、下载地址https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/debugger-download-tools#small-classic-windbg-preview-logo-debugging-tools-for-windows-10-windbg 2、安装 记录WinDbg安装目录&#xff0c;待会…

windbg 调试崩溃

前言 windbg 是非常强大的调试工具&#xff0c;但是在使用windbg 进修调试时候&#xff0c;很多的命令不知道如何使用。文章简单介绍如何使用windbg进行调试 https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/debugger-download-tools 一、Windows 调试入…

windbg 调试学习

符号服务器地址 微软的公共符号服务器地址&#xff1a;Symbol information 五种符号加载状态 1. deferred: 延迟加载&#xff0c;现在还不需要使用这个模块的符号&#xff0c;将在后面需要的时候加载 2. no symbols: 没有找到相应的符号文件 3. export: 没有找到symbols,但…

Windbg调试(使用方法)

一、Windbg版本信息 Windbg分32位和64位版本&#xff0c;32位程序应使用32位Windbg调试&#xff0c;64位程序应64位Windbg调试。 若想使用64位的Windbg分析32位的程序, 使用如下命令进行CPU模式的切换&#xff1a; .load wow64exts;!sw//例如&#xff1a; 1.查看线程停在哪里…

Windebug

1、 安装drwtsn32&#xff08;Dr.Watson win7没有了&#xff09; 用户可以通过drwtsn32命令&#xff0c;查看dmp文件会被保存在何处。 2、 安装windbg,Windbg下载地址&#xff1a; http://www.microsoft.com/whdc/devtools/debugging/default.mspx 3、 设置windbg A、符号表路径…

windbg调试分析dump工具,使用windbg分析Qt崩溃原因

目录 windbg下载和安装windbg调试exe可执行文件windbg分析dump文件 在非开发环境&#xff0c;windbg工具可以对.exe可执行文件进行调试&#xff0c;也可以分析dump文件&#xff0c;是release版本运行模式解决bug的神器。 windbg下载和安装 一般在安装windows系统软件时会安装w…

02-windows调试工具(DebugDiag使用)

工具下载地址&#xff1a;https://www.microsoft.com/en-us/download/details.aspx?id49924csdn的下载地址&#xff1a;https://download.csdn.net/download/qq_37103755/87089384DebugDiag的简介&#xff1a; DebugDiag 工具主要用于帮助解决如挂起、 速度慢、 内存泄漏或内存…

如何使用Debugging Tools for Windows (windebug)简单的使用心得

1.安装debug工具 ​ 下载页面地址&#xff1a;http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx 选择合适的版本安装 ​ 2.安装Symbols&#xff08;特征库&#xff09; 建议可以多安装以免出现分析不出来的情况 ​ 下载地址&#xff1a;http://www.mi…

WinDebug快速使用及调试注意事项

参考文献&#xff1a; http://www.cnblogs.com/killmyday/archive/2010/03/14/1685331.html WinDbg是微软发布的一款强大的源码级调试工具&#xff0c;支持用户态和内核态两种模式的调试&#xff0c;支持Dump文件分析、内存泄露诊断、同时支持跨机器远程附加进程调试。在具体…

Windows下调试工具Windbg入门

前言 Windbg简单来说就是一个Windows下对用户态/内核态的程序进行调试&#xff0c;以及对Core Dump文件的分析。对于Crash&#xff0c;资源泄露&#xff0c;死锁等问题的分析&#xff0c;Windbg是一个强有力的利器。 一、下载 微软官网提供的Windbg为windows10版本&#xff…

这可能是最详细的 Windows Debug 详解 了

Debug概览 Debug是什么&#xff1f; debug是Windows 16位或者32位机器上的一款调试工具。也就是说&#xff0c;在WindowsXP及以前的机器上都有debug&#xff0c;直接WinX debug就可以调出&#xff1b;在之后的32位机也有&#xff1b;但是在之后的64位机器上不存在&#xff0c…

Debugging Tools for Windows (WinDbg)的使用

安装 可能大家安装时&#xff0c;直接从外部的网址下载或者拷贝&#xff0c;一般这也是可行的&#xff0c;只不过可能安装的不是最新版本&#xff0c;或者找不到和自己当前系统匹配的版本&#xff0c;所以最简单的方法还是去微软的官网下载 下载地址: Download Debugging tool…

Windows调试工具入门 — windebug

一、 引子 Debugging Tools for Windows是微软发布的一套用于软件调试的工具包(后面如果没有指明&#xff0c;那么我会使用WinDbg来作为这一套调试工具的简称)。我第一次接触是在三年前的一个内核驱动项目&#xff0c;由于进行了IDT中键盘鼠标中断的Hook&#xff0c;使用Softi…

Windebug专题

5 解决问题案例 !cs、~~[TID]&#xff08;经典死锁&#xff09; 随手写的&#xff1a; #include <windows.h > CRITICAL_SECTION cs1; CRITICAL_SECTION cs2; DWORD __stdcall thread1(LPVOID lp) { EnterCriticalSection(&cs1); Sleep(10); EnterCriticalSection(…

winDebug 调试

1.背景 最近由于线上的程序发生了死锁&#xff0c;而且重现的概率很低&#xff0c;正好客户反馈一个任务超时了&#xff0c;登上线上环境发现有一个“僵尸”进程&#xff0c;占用内存不波动&#xff0c;cpu仍在占用&#xff0c; 那么用创建转储文件&#xff0c;用windbg调试吧…

ZedGraph类库之基本教程---PieSampleDemo.cs(画饼)

ZedGraph类库之基本教程 PieSampleDemo.cs介绍 这节我把饼形图粗略的介绍一下&#xff0c;如下图。 PieSampleDemo.cs&#xff08;画饼&#xff09; using System; using System.Drawing; using System.Collections; using ZedGraph; namespace ZedGraph.Demo { ///<sum…

Winforn中设置ZedGraph曲线图的属性、坐标轴属性、刻度属性

场景 C#窗体应用中使用ZedGraph曲线插件绘制图表&#xff1a; https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/99716066 在上面已经实现基本的曲线图之后&#xff0c;效果如下&#xff1a; 当然这不是我们的效果&#xff0c;还要对其属性进行设置。 但是毕竟其…