我构建了一个HTML5多人游戏,依赖于在服务器和客户端之间进行合理准确的时间同步。在大多数情况下,我使用的算法非常准确 - 它只是估计客户端 - 服务器时间增量是什么,即服务器上的当前时间与客户端上的当前时间之间的差异。例如,如果服务器时间比客户端时间提前5秒,则时间增量为5000毫秒。
客户端和服务器(node.js)都是用Javascript编写的。该算法的工作原理如下:
在客户端上记录时间:
var clientTime = Date.now();
Ping服务器。当服务器收到消息时,立即发送一个只包含一件事的响应:收到消息时服务器上的时间。
var serverTime = Date.now();
// Send serverTime to the client
当客户端收到服务器响应时,立即记录时间:
var clientTime2 = Date.now();
现在,我们知道当服务器收到消息时,客户端时间必须介于clientTime
和clientTime2
之间。
如果服务器在客户端时间为clientTime
时收到消息(即客户端 - >服务器请求以某种方式花费0ms),则时间增量为
var delta1 = (serverTime - clientTime);
如果服务器在客户端时间为clientTime
时收到消息(即服务器 - >客户端响应以某种方式花费0ms),则时间增量为
var delta2 = (serverTime - clientTime2)
。
因此,我们可以有把握地说,时间差值介于delta1
和delta2
之间。现在,重复这个过程很多次,每次根据你得到的结果缩小范围,你就可以得到一个非常好的时间差估计。
我已经在7种不同的浏览器和多台机器上测试了数百次,并且从未对它有任何问题。它永远不会不一致。
但问题是,我的服务器日志显示,偶尔有一些人会得到非常不一致的时间同步结果。以下是一个玩家时间同步的实际示例:
客户端经历了上述算法的 74 周期,并成功将可能的时间增量范围缩小到:[ - 186460,-186431],没有一个不一致。 29ms准确度。
在第74个周期,可能是第74个周期后的几秒钟,客户计算出可能的时间增量范围为:[ - 601,-596]。 5ms准确度,除了它与过去的74个周期非常不一致:它 3分钟关闭!
我会把这归咎于疯狂的边缘情况,除了它每天发生几乎100次......怎么会发生这种情况?使用Date.now()
时是否有任何错误?
答案 0 :(得分:1)
您的困难在于您依赖于估计往返服务器的往返时间,因为互联网的往返时间会有所不同。有时,这种差异将出乎意料地大幅度增加,例如临时拥塞和大型路由器缓冲区延迟单个消息的时间远远超过正常情况。 (Cf" bufferbloat"。)
你的第二个困难是你正在使用自我报告的客户时间,这意味着如果客户有一个奇怪的时钟,你会觉得往返估计出错了。正如另一张海报所指出的那样,互联网时间协议有时会迅速从一个时钟中恢复,以便从当地的计时异常中纠正。
听起来是您需要在代码中进行一些过滤,并考虑以前的结果,这样当您收到异常结果时,您不会立即接受它为真。
答案 1 :(得分:1)
performance.now()
代替Date.now()
,因为performance.now()
单调增加且不受时钟漂移的影响。看到评论,感谢大家的帮助!