问题描述
我使用 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(使用 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 偏移量:
'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)