不一致的Date.now()数值

3
我开发了一个基于HTML5的多人游戏,它需要在服务器和客户端之间保持合理准确的时间同步。大多数情况下,我使用的算法非常精确——它只是估计客户端与服务器之间的时间差,即服务器当前时间与客户端当前时间之间的差异。例如,如果服务器时间比客户端时间早5秒,时间差就是5000毫秒。
客户端和服务器(node.js)都是用Javascript编写的。该算法的工作原理如下:
  1. Record time on the client: var clientTime = Date.now();

  2. Ping the server. When the server receives the message, it immediately sends a response containing just one thing: the time on the server when the message was received.

    var serverTime = Date.now();
    // Send serverTime to the client
    
  3. When the client receives the server response, immediately record the time: var clientTime2 = Date.now();

现在,我们知道当服务器接收到消息时,客户端时间必须在clientTime和clientTime2之间。
如果服务器在客户端时间为clientTime时(即客户端->服务器请求以某种方式花费了0ms),则时间差为var delta1 =(serverTime-clientTime);
如果服务器在客户端时间为clientTime2时(即服务器->客户端响应以某种方式花费了0ms),则时间差为var delta2 =(serverTime-clientTime2)。
因此,我们可以安全地说,时间差在delta1和delta2之间。现在,重复这个过程多次,每次根据得到的结果缩小范围,就可以相当准确地估计时间差。
我已经在7个不同的浏览器和多台机器上测试了数百次,并且从来没有出现过任何问题。它从未不一致过。
问题在于,我的服务器日志显示,偶尔会有一些人得到极度不一致的时间同步结果。以下是一个玩家时间同步的实际示例:
客户端经历了74个以上算法周期,并成功将可能的时间差范围缩小到:[-186460,-186431],没有任何不一致。29ms的精度。
在第75个周期,在第74个周期之后的几秒钟内,客户端计算出可能的时间差范围为:[-601,-596]。5ms的精度,除了与过去74个周期极其不一致外。它比过去慢了3分钟!
我会把这归咎于疯狂的边缘情况,但几乎每天都会发生这种情况……怎么可能呢?在使用Date.now()时是否有可能出现任何错误?

2
这些随机的不一致性是否与其他数据相关?也许是一般的时间(在您的服务器或客户端的时区)?是其中一个(或两个?!)正在执行互联网时间同步并大多修复漂移吗? - rockerest
我尝试了基本的谷歌搜索,但没有找到好的答案,有人能向我解释更多关于互联网时间同步的知识吗?我认为可以安全地假设一个具有良好互联网连接的计算机永远不会随机跳跃3分钟的时间... 另外,我的服务器是从DigitalOcean购买的Ubuntu并托管在他们的纽约中心,如果这有关系的话。 - Victor Zhou
@Victor Zhou 对于大多数人来说,期望时间偏差在3分钟内是完全合理的。时间同步通常只会发生一次(比如说一周,为了论证而言,但它可能会更长或稍短,几乎永远不会像每天一样频繁)。在这段时间之间,时钟只能通过处理器每1000毫秒的滴答声保持同步,但这是个微妙的事情,无数的变量都可能会影响它。功率过低或过高的处理器可能会不正确地计算时间,不良处理器可能会错过滴答声等等。 - rockerest
@rockerest 哇,我不知道啊。我记得有一次看到一个客户的日志,连续出现了20个左右的不一致,每个差大约1秒钟。这可能是因为他的时钟逐渐自我校正,所以时间差不断增加吗? - Victor Zhou
@dandavis,你怎么知道它不会漂移,你有来源吗?如果是这样的话,那将是一个很好的解决方案。 - Victor Zhou
显示剩余9条评论
2个回答

2

使用performance.now()代替Date.now(),因为performance.now()是单调递增的,不会受到时钟漂移的影响。感谢大家的帮助,请查看评论!


1
你的困难在于,你依赖于对服务器往返时间的估计,而互联网往返时间具有方差。有时这个方差会意外地很大,比如在临时拥塞和大型路由器缓冲区延迟单个消息远远超过正常情况下的情况。(参见“bufferbloat”)。
你的第二个困难是你正在使用自报告客户端时间,这意味着如果客户端的时钟出现问题,它会像往返估计出错一样显示给你。正如另一个帖子所指出的那样,互联网时间协议有时会快速调整时钟,以纠正本地时间跟踪异常。
听起来你需要在你的代码中添加一些过滤器,考虑到以前的结果,这样当你得到异常结果时,你不会立即接受它为真。

有过滤,我不会自动认为它是真的。算法完全不关心任何形式的拥塞或延迟。即使客户端->服务器需要50毫秒,服务器->客户端需要6000毫秒,仍然不会出现不一致(我已经进行了测试以进行双重检查)。我能想到不一致发生的唯一方式是,如果客户端或服务器时间因为某种原因瞬间跳回/前进...... - Victor Zhou
这是关于网络时间协议如何处理时间变化的介绍,例如当本地时钟偏离实际时间时。http://www.ntp.org/ntpfaq/NTP-s-algo.htm - vielmetti
谢谢,你有没有推荐一个具体的章节可以阅读?这是一篇相当长的文章哈哈。 - Victor Zhou
是的!网络时间协议非常古老,文档也相应陈旧。请查看关于“6.操作系统时钟接口”的部分。 - vielmetti
1
时间同步真的很难。不要假设你自己编写的实现方式就是正确的。认真研究NTP及其后续版本。 - Jim Garrison

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接