NTP协议

NTP(Network Time Protocol)

NTP(Network Time Protocol):是一种用于同步计算机系统时间的协议。

NTP 通过分层的时间服务器架构,确保网络中所有设备的时间保持一致,精度可达毫秒甚至微秒级别,是互联网上最广泛使用的时间同步协议。

NTP就是上述过程的网络版:

设备(客户端) → 通过网络 → 问NTP服务器 → "现在几点?"

NTP服务器 → 回复一个精确时间戳 → 设备据此调整时钟

NTP报文格式(48B):

字节偏移 字段名称 大小 说明
0 LI + VN + Mode 1字节 闰秒+版本+模式
1 Stratum 1字节 服务器层级
2 Poll 1字节 轮询间隔
3 Precision 1字节 精度
4-7 Root Delay 4字节 根延迟
8-11 Root Dispersion 4字节 根离散度
12-15 Reference ID 4字节 参考源标识
16-23 Reference Timestamp 8字节 上次同步时间
24-31 Origin Timestamp 8字节 客户端发送时间(原样返回)
32-39 Receive Timestamp 8字节 服务器收到时间
40-47 Transmit Timestamp 8字节 服务器发送时间

各字段具体含义:

字段名称 长度 含义说明
LI (Leap Indicator) 2 bits 闰秒指示器 。警告客户端关于即将到来的闰秒(由于地球自转不均匀,UTC偶尔需要加1秒或减1秒)。 00: 无警告 01: 最后一分钟是59秒 10: 最后一分钟是61秒 11: 时钟未同步(告警状态)
VN (Version Number) 3 bits NTP 版本号 。目前常用的是版本 4(二进制 100)。
Mode 3 bits 工作模式 。标识这个包是客户端发的还是服务端发的。3: 客户端 4: 服务器 6: 广播/组播等等
Stratum 8 bits 层级 。表示时钟距离标准参考时钟(如原子钟、GPS)有多远。0: 无效或 unspecified 1: 直接连接参考时钟(如带GPS的NTP服务器) 2-15: 逐级向下同步的层级 16: 表示时钟未同步
Poll 8 bits 轮询间隔 。表示连续两次NTP请求之间的最大时间间隔。这是一个以2为底的指数(如6表示 64秒)。
Precision 8 bits 精度 。表示本地时钟的精度,也是以2为底的指数(通常为负数,如-10表示约1毫秒精度)。
Root Delay 32 bits 根延迟 。从当前服务器到主参考时钟(Stratum 1)之间的总往返延迟时间(毫秒级)。
Root Dispersion 32 bits 根离散度 。表示相对于主参考时钟的最大误差估计值。
Reference ID 32 bits 参考标识符 。标识当前服务器是从哪里同步时间的。Stratum 1时通常是GPS、ATOM等ASCII码; Stratum 2及以上时通常是上游服务器的IP地址。
Reference Timestamp 64 bits 参考时间戳 。本地时钟最后一次被设置或校正的时间(NTP时间戳格式)。
Origin Timestamp 64 bits 源时间戳 。客户端发送请求时的本地时间(NTP时间戳格式)。
Receive Timestamp 64 bits 接收时间戳 。服务器收到客户端请求时的本地时间(NTP时间戳格式)。
Transmit Timestamp 64 bits 发送时间戳 。服务器把响应包发给客户端时的本地时间(NTP时间戳格式)。

NTP 是如何利用这些时间戳计算网络延迟的?

NTP之所以需要这4个时间戳,是为了消除网络传输带来的误差。计算公式如下(T1到T4均为时间点):

T1 (Origin) = 客户端发包时间

T2 (Receive) = 服务器收包时间

T3 (Transmit) = 服务器回包时间

T4 ( 到达客户端时间) = 客户端收到回复时的本地时间(这个不在包里,是客户端自己记录的)

通过这4个时间点,客户端可以算出:

  1. 网络往返延迟 = (T4 - T1) - (T3 - T2)
  2. 客户端与服务器的时间差 = ((T2 - T1) + (T3 - T4)) / 2

算出时间差后,客户端就会将这个差值(可能带有小数,即NTP时间戳的后32位)转换为操作系统支持的Unix时间戳格式,并调整系统时钟。

当NTP客户端和服务器(网络层)通过网络通信时,它们在数据包中交换的只有NTP时间戳 。但在操作系统层面需要转换 :当操作系统(如Linux/Windows)接收到NTP服务器的回复后,操作系统的内核或NTP守护进程(如ntpd/chronyd)会将收到的"NTP时间戳"在内存中转换为"Unix时间戳",然后再设置到系统时钟中:

NTP时间戳

为什么有NTP时间戳和其他时间戳(以Unix为例)?

它们诞生于不同的历史背景,服务于不同的层级(网络层 vs 操作系统层):

1. NTP时间戳(诞生于1985年之前)

结构 :64位长整型。前32位表示秒数 ,后32位表示小数秒(即纳秒/皮秒级精度),从1900年1月1日 00:00:00 UTC开始。

原因:NTP被设计用于跨网络同步高精度时间,需要极高的分辨率(小数秒)。选择1900年是因为当时设计NTP时,32位无符号整数能表示约136年,刚好覆盖到2036年(即NTP的"2036年溢出问题"),在当时看来是足够安全的。

2. Unix 时间戳(诞生于1970年代初)

结构 :通常是一个32位或64位整数,表示自1970年1月1日 00:00:00 UTC以来的整秒数(早期Unix系统不需要亚秒级精度)。

原因:Unix系统在设计时,为了在有限的硬件资源下方便计算日期,选择了1970年作为起点,且只记录整秒。著名的"Y2038问题"就是指32位Unix时间戳在2038年会溢出(靠物理升级到 64 位解决)。

NTP时间戳是为了网络高精度传输 设计的;Unix时间戳是为了操作系统内部简单计算设计的。两者起点不同、精度不同,所以需要转换。

其他的时间戳还有很多,比如:

  • Windows 系统时间戳:起点是1601年1月1日,以100纳秒为间隔,存储在64位结构中。
  • PTP 时间戳(IEEE 1588 精密时间协议):起点是1970年1月1日(TAI国际原子时,非UTC),精度可达纳秒甚至皮秒级,用于金融交易和5G基站等高精尖领域。
  • GPS 时间戳:起点是1980年1月6日,连续计数不含闰秒。
  • Mac/Apple Cocoa 时间戳:起点是2001年1月1日,以秒为单位。

所以在windows上用Python (是一门跨平台语言,在底层实现CPython中做了转换)写NTP相关的代码时是转化成Unix时间戳,到 C/C++ (调用Windows API)转化成Windows系统时间戳。

NTP-Unix转换公式:

NTP时间戳 = (Unix时间戳 + 2208988800) << 32 | 小数部分

Unix时间戳 = (NTP时间戳 >> 32) -- 2208988800

NTP的2036年溢出问题

NTP的2036年溢出问题的解决(v4版)并没有修改数据包结构(为了向后兼容,老设备依然能解析)。它的解决逻辑非常巧妙,记录在 RFC 5905 中(软件层面的"纪元推断"和加法修正),可以参考链接如下:

https://www.rfc-editor.org/info/rfc5905/#page-13

1.定义新的纪元

当 2036 年到来,32位秒数溢出归零后,NTP 并不认为这是 1900 年,而是定义这是一个新的纪元。(有一种三体的感觉)

第一个纪元:1900年 - 2036年

第二个纪元:2036年 - 2172年

第三个纪元:2172年 - 2308年

以此类推,每 136 年一个纪元。

2.客户端如何推断现在是哪个纪元?

NTP 协议规定:客户端和服务器在通信时,默认双方处于同一个纪元。客户端在发包时,知道自己本地的大致时间(比如客户端知道现在是 2036 年 5 月)。当它收到服务器发来的时间戳(秒数部分是一个很小的数字,比如 1000万秒)时,客户端会这样算:

"现在是 2036 年,处于第二个纪元。服务器发来的小数字,一定是第二个纪元里的时间。"

于是客户端自动把这个小数字加上 2^32秒的偏移量,还原出正确的时间。

3.跨纪元过渡期怎么办?(2035年-2037年)

但如果在 2035 年,客户端(还在第一个纪元末尾)向服务器请求,但服务器因为某种原因已经翻到了第二个纪元(返回了很小的秒数),客户端怎么判断?

规则: 如果客户端收到的时间戳,比客户端自己当前的时间小很多(比如相差超过 68 年),客户端就会自动给收到的时间戳加上一个 2^32(约136年),看看加上之后是不是合理。如果加上之后刚好和本地时间接近,就说明服务器已经进入了下一个纪元。

NTP采用UDP协议

NTP传输采用的是UDP协议,因为NTP 不需要 TCP 的"可靠性"和"顺序性",它需要的是 UDP 极低的协议开销和网络延迟以及"无状态的并发处理能力"。对于时间同步来说,"快速拿到一个最新的时间快照"比"确保每一个包都送达"重要得多。

参考资料

NTP 协议 | 菜鸟教程

https://www.rfc-editor.org/info/rfc5905

相关推荐
weixin_727535627 小时前
HTTP 八股文:从三次握手到浏览器渲染的硬核拆解
网络·网络协议·http
复园电子8 小时前
USB Over IP技术详解:基于USB服务器实现USB设备远程访问、重定向与集中管理
服务器·网络协议·tcp/ip
TlSfoward8 小时前
TLSFoward 能帮你看到什么 TLSFOWARD抓包工具
服务器·爬虫·网络协议·https
kaixin_learn_qt_ing9 小时前
单网卡绑定多个 IP 地址
网络·网络协议·tcp/ip
摇曳的精灵10 小时前
HTTP 与 MCP:不是替代,而是分层
网络·网络协议·http·mcp
AI人工智能+电脑小能手10 小时前
【大白话说Java面试题 第214题】【10_网络协议篇】第5题:说一下 TCP 协议的三次握手和四次挥手
java·网络协议·tcp·三次握手·四次挥手
黑桃小柒71 天前
小,最终显示为TCP WINDOW FULL,TCP ZeroWindow。 仔细分析了下LWIP源码,还以为是内存管理出了问题,跟 ...
网络·网络协议·tcp/ip
便利店10241 天前
TCP 为什么有时故意跑不快?拥塞控制与滑动窗口一次讲清
服务器·网络协议·tcp·拥塞控制
组合缺一1 天前
Solon 的 10 种 HTTP 服务器:改一行依赖,换一个引擎
java·服务器·网络协议·http·solon