应该使用 NTP 时间服务器的什么时间来设置我的时钟?

如何解决应该使用 NTP 时间服务器的什么时间来设置我的时钟?

我使用 Python3 和 ntplib https://pypi.org/project/ntplib/ 来查询 NTP 时间服务器。效果很好,但我不知道使用提供的哪些时间变量来正确设置计算机上的时钟。

阅读第 23 页的 NTP https://tools.ietf.org/html/rfc5905 的 IETF 文档,我选择了其中之一:

Transmit Timestamp (xmt):响应离开时服务器上的时间 对于客户端,采用 NTP 时间戳格式。

Destination Timestamp (dst):回复时在客户端的时间 从服务器到达,采用 NTP 时间戳格式。

我的解释是xmt是服务器发送时的正确时间,这似乎表明我仍然需要为从服务器到我的计算机的传输时间添加时间延迟?

我不清楚 dst 时间的定义。这可能意味着:

  1. 这是已添加传输时间的 xmt 时间,因此是用于设置时钟的正确时间
  2. 或者它是 NTP 数据包到达时我的时钟时间。如果我的时钟是错误的,那将不是正确的使用时间

是哪一个?

我认为 #1(使用 dst)更有意义,但在网上找到的大多数脚本都使用普通的 xmt。就 ntplib 的代码而言,这意味着:

    client = ntplib.NTPClient()
    resp   = client.request(server,version=3)
    xmt    = resp.tx_time     # for the use of xmt
    # or:
    dst    = resp.dest_time   # for the use of dst

在一些测试运行中,dst 总是比 xmt 晚 3 ... 30 毫秒,在使用本地、区域或全球 NTP 服务器时没有明显的模式。

所以不是很多,但我不想做出不合逻辑的选择。

解决方法

NTP 响应有 5 个时间戳

字段名称 姓名 说明
ref_timestamp 参考时间戳 系统时钟上次设置或更正的时间,采用 NTP 时间戳格式
orig_timestamp 原始时间戳(T1) 请求离开服务器时在客户端的时间,采用 NTP 时间戳格式。
recv_timestamp 接收时间戳(T2) 请求从客户端到达服务器的时间,采用 NTP 时间戳格式。
tx_timestamp 传输时间戳(T3) 响应离开客户端时服务器上的时间,采用 NTP 时间戳格式。
dest_timestamp 目的地时间戳(T4) 响应从服务器到达时在客户端的时间,采用 NTP 时间戳格式。

每个时间戳字段都有一个关联的时间字段,该字段是作为系统时间的时间戳,即自纪元以来的时间(以秒为单位)。

字段名称 说明
ref_time 参考时间戳作为系统时间
原点时间 将时间戳作为系统时间
recv_time 接收时间戳作为系统时间
tx_time 传输时间戳作为系统时间
dest_time 目标时间戳作为系统时间

偏移量是您的本地时钟与 NTP 服务器时钟之间的差值,即以秒为单位校正本地时钟以匹配服务器时钟的量。

偏移量 = ((recv_timestamp - orig_timestamp) + (tx_timestamp - dest_timestamp))/2

偏移量 = ((T2 - T1) + (T3 - T4))/2

示例:

import ntplib
from datetime import datetime

client = ntplib.NTPClient()
server = '0.pool.ntp.org'
resp = client.request(server,version=3)

print("offset",resp.offset)
print("orig_time:",datetime.utcfromtimestamp(resp.orig_time).strftime('%Y-%d-%m %H:%M:%S.%f'))
print("recv_time:",datetime.utcfromtimestamp(resp.recv_time).strftime('%Y-%d-%m %H:%M:%S.%f'))
print("tx_time  :",datetime.utcfromtimestamp(resp.tx_time).strftime('%Y-%d-%m %H:%M:%S.%f'))

输出

offset 0.009677410125732422
orig_time: 2021-06-01 00:06:10.553593
recv_time: 2021-06-01 00:06:10.665957
tx_time  : 2021-06-01 00:06:10.665980

偏移为正值意味着服务器时钟比本地时钟提前该秒数。负值表示本地时钟提前。

在上面的输出中,服务器在 00:06:10.665957 (recv_time) 收到时间请求,并在 00:06:10.665980 (tx_time) 响应,耗时 23 微秒完成。

,

所有各种时间戳的知识对于理解如何更改我的计算机的时间以与 NTP 时间服务器一致并不是很有帮助。

经过几天的摆弄,我相信我找到了解决方案。答案是:不要使用任何时间戳——而是只使用偏移量!并且偏移量已经由 ntplib 为您计算出来了。

这令人惊讶,因为大多数人使用其中一个时间戳(主要是 xmt),而且,我可能错过了它,但我从未见过有人使用偏移量。

让我们看一个例子。我使用了 3 个 NTP 服务器:一个在亚洲(最偏远),一个在欧洲(中距离),一个在德国(最近)。数据包的往返时间变化很大,从几毫秒到 200 毫秒不等,与近距离或远距离目标无关。我选择了一个例子来证明我的观点(代码在最后)。我从时间戳中减去原点,所以所有时间都相对于它们各自的原点。

    NTP Server            Type t-stamp          sec 
    asia.pool.ntp.org     orig                  0.000
    asia.pool.ntp.org     recv                  0.004
    asia.pool.ntp.org     tx                    0.004
    asia.pool.ntp.org     dest                  0.014
    asia.pool.ntp.org     offset               -0.003

    europe.pool.ntp.org   orig                  0.000
    europe.pool.ntp.org   recv                  0.008
    europe.pool.ntp.org   tx                    0.008
    europe.pool.ntp.org   dest                  0.020
    europe.pool.ntp.org   offset               -0.002

    de.pool.ntp.org       orig                  0.000
    de.pool.ntp.org       recv                  0.008
    de.pool.ntp.org       tx                    0.008
    de.pool.ntp.org       dest                  0.019
    de.pool.ntp.org       offset               -0.002

在亚洲示例中,往返总时间为 14 毫秒。时钟差在发送时为 4ms,在接收时为 10ms。因此偏移量为 (4 - 10)/2 = -3ms。

现在要做的所有重要假设是往返的旅行时间相同!那么我可以说如果我把这个偏移量加到我的电脑时间里,两个时间都会变得一样!

欧洲同样适用:往返 20 毫秒,偏移:(8 - 12)/2 = -2 毫秒,德国:往返 19 毫秒,偏移:(8 - 11)/2 = -1.5 毫秒。

由于偏移量已经由 lib 给出,所以您要做的就是将这个有符号的偏移量添加(!,而不是减去)到您的计算机时间。

然后我在使用德国(最近的)NTP 服务器大约半小时时记录了 ntplib 偏移量:

enter image description here

'Ambient' 是 ntplib 报告的偏移量,以毫秒为单位。

看起来我的电脑设置得很好,它的时间大多在 NTP 时间的 5 毫秒内。但是有一些长达 70 毫秒的显着偏移!在一个程序中,我本可以选择这个 - 显然是错误的 - 时间。

明显的问题:如何确保在我的时钟调整中不会出现异常值?

代码的基本部分:

    for i,ntpserver in enumerate(NTP_SERVERS):
    ntps     = []
    client   = ntplib.NTPClient()
    try:
        response = client.request(ntpserver,version=4,timeout=0.5) # latest NTP version = 4

        orig = response.orig_time
        recv = response.recv_time
        tx   = response.tx_time
        dest = response.dest_time
        offs = response.offset
        ntps.append( ["orig",orig - orig])
        ntps.append( ["recv",recv - orig])
        ntps.append( ["tx  ",tx   - orig])
        ntps.append( ["dest",dest - orig])
        ntps.append( ["offset",offs])
       
        for  a in ntps:
            print("{:20s}  {:15s}  {:10.3f} ".format(ntpserver,a[0],a[1]))
       
    except Exception as e:
        msg = fncname + "FAILED with Exception: {}".format(e)
        edprint(msg)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-