bigywater.tech
网络协议 · 100

NTP:时间同步报文与偏移计算

计算机之间怎么对时?NTP 用一对请求和应答里的四个时间戳算出本地时钟的偏移和网络延迟。这篇手工发 NTP 请求给真实服务器,逐字段解析,验证偏移公式,并用一个自己写的服务器证明路径不对称会让对时出现不可消除的误差。

48 字节
NTP 请求和应答的报文长度,UDP 端口 123
0.09 ms
对称往返下自写服务器上 offset 与 250 ms 真值的误差
−41.98 ms
返程多 80 ms 时 offset 的误差,约为不对称量的一半

报文

实测 · 本机执行输出
  请求 48 字节,首字节 0x23 = LI 0 | 版本 4 | 模式 3(客户端);只有 transmit 时间戳(最后 8 字节)有值;应答 48 字节,来自 203.107.6.88
  应答头:LI=0(0=正常),版本 4,模式 4(4=服务器),stratum 3,poll 0(2^0 秒),precision -25(2^-25 秒 ≈ 30 ns)
  根延迟 root delay 10.60 ms,根离散度 root dispersion 10.19 ms,参考标识 100.107.25.114(stratum ≥ 2 时是上游服务器的 IPv4 地址,stratum 1 时是 4 字符的时钟源名如 GPS)
  四个时间戳(都是 64 位:32 位整数秒 + 32 位小数,起点 1900-01-01):
    T1 originate(我发出请求的时刻,被服务器原样抄回)= 1792062857.327713,与我记录的 T1 1792062857.327713 差 0.00 µs(32 位小数的精度 ≈ 0.23 ns,但是 Python float 只有微秒量级)
    T2 receive(服务器收到)= 1792062857.344211;T3 transmit(服务器发出)= 1792062857.345903;T4(我收到)= 1792062857.352936
    服务器的处理时间 T3 − T2 = 1692.5 µs;往返时间 T4 − T1 = 25.22 ms
  偏移 offset = ((T2 − T1) + (T3 − T4)) ÷ 2 = 4.73 ms;延迟 delay = (T4 − T1) − (T3 − T2) = 23.53 ms

48 字节:头部(LI、版本、模式、stratum 等)加四个 64 位时间戳。

偏移与延迟

实测 · 本机执行输出
    服务器                  stratum  参考标识           偏移中位(ms)  偏移范围(ms)           延迟中位(ms)  延迟范围(ms)
    ntp.aliyun.com            3     100.107.25.114         1.59   [  -1.00,    4.59]        19.93   [19.02, 22.69]
    ntp.tencent.com           2     100.122.36.196        -1.56   [ -11.31,    5.63]        52.51   [46.47, 71.86]
    time.cloudflare.com       3     10.191.8.4             6.75   [  -6.76,   11.10]       173.51   [163.90, 179.46]
    pool.ntp.org              3     10.127.8.21            5.40   [   2.64,   13.20]       175.30   [157.16, 186.56]
  各服务器给出的偏移中位数 [1.6, -1.6, 6.7, 5.4] ms:它们互相一致(差 8.3 ms)说明本机时钟相对标准时间的偏移就是这个量级;单次测量的抖动(范围)远大于偏移的真值误差,所以 NTP 客户端要多次采样、取延迟最小的那一次

offset = ((T2−T1)+(T3−T4))÷2,delay = (T4−T1)−(T3−T2)。真实服务器延迟 20~175 ms,偏移 −1.6~6.7 ms。

公式验证

实测 · 本机执行输出
  对称往返(每方向 50 ms):offset 中位 250.09 ms(服务器拨快了 250 ms;本机相对服务器慢 → offset 为 +250);delay 中位 101.31 ms(≈ 2 × 50 ms)
  误差:offset 与真值 250 ms 之差 0.09 ms

对称路径下,250 ms 的时钟差被测到 250.09 ms。

路径不对称

实测 · 本机执行输出
  返程多 80 ms 后:offset 中位 208.02 ms(真值仍是 +250 ms);delay 中位 184.53 ms
  offset 的误差 = -41.98 ms,恰好是不对称量 80 ms 的一半的相反数(-40 ms):NTP 把整个往返时间平分给两个方向,所以它测不出也修正不了不对称——这是网络对时精度的根本限制

NTP 把往返平分,不对称的一半会变成偏移误差(−41.98 ms)。

分步演示

NTP
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64(Apple 芯片)/ Python 3.14.8 / tcpdump(libpcap)/ scapy 2.8 / dpkt / dnspython / h2 / aioquic / OpenSSL / curl 8.7.1;抓包在 lo0(环回)上做,少数实验用真实网卡 en0(这台机器上 en0 是 Wi-Fi,tcpdump 看到的是系统转成以太网格式的帧,所以文中的“以太网头”都指这个 14 字节的头)或访问公网(会在文章里注明);需要丢包 / 延迟 / 限速时,用一个只作用于 lo0 端口 9400-9499 的 dummynet 辅助脚本(netlab);所有服务端都是本机自己起的进程;时间戳取自 tcpdump 的 -tt,数字每次运行会有小幅抖动。第一、二节是对 ntp.aliyun.com、ntp.tencent.com、time.cloudflare.com、pool.ntp.org 的真实 NTP 查询(每个服务器 8~9 次,间隔 1 秒,没有高频轰炸);time.apple.com、cn.pool.ntp.org 等在我的探测里超时,没有使用;本机时钟偏移的绝对值受本机时钟自身的校准状态影响;第三、四节是自写服务器,时间戳精度受 Python float 限制
    • 没有验证:NTP 的过滤、选择与聚类算法(ntpd / chrony 怎么在多个服务器中选);时钟纪律(渐进调整 slew 与跳变 step);Kiss-o'-Death 限速报文;NTS(NTP 的认证);放大攻击(monlist);PTP;闰秒处理;本机 macOS 的 timed 实际用哪些服务器
    京ICP备2026067502号-1京公网安备11010502063487号