Post

时间同步机制

时间同步机制

读者知道游戏客户端连接游戏服务端发送的第一个请求是什么吗?没错,是发送一个请求以获取服务端的发送回复的时间戳。这个请求的逻辑很简单,客户端发送ServerTime请求,服务端收到请求,将收到请求的时间戳写入回复中发送给客户端。读者知道这个请求的目的是什么吗?还有,服务端会客户端的每个请求的回复都会带上时间戳。这两件事都是为了同一个目的:分布式时间同步。

时间同步机制

网络一定存在延迟性,对于时间同步背后的发展历史,可以参见我之间翻译的Doom3网络架构一文。客户端发送请求,收到回复,此时回复所展现的服务端状态一定是落后的。如何解决这个问题呢?

第一步就是建立统一的时间基准,这个就是第一件事所做的事情。客户端发送请求服务端时间的时候记录一个本地发送时间ClientSendTime,收到回复的时候取出回复中带有的ServerTimeInitial, 并记录收到回复的本地时间ClientReceiveTime。首先计算RTT: RTT = ClientReceiveTime - ClientSendTime。得到单程损失:OneWay Delay = RTT / 2。客户端获得到这两个参数后,就可以估算出客户端后续发出时间获取请求瞬间服务端的时间:

EstimatedServerTime = ServerTimeInitial - OneWay Delay

然后就可以计算初始时间偏移量:InitialOffset = EstimatedServerTime - ClientSendTime。简单理解这个偏移量就是把客户端的时间往前(后)播使得客户端时间和服务端时间保持接近。

得到初始时间偏移量后,第二步就是客户端建立服务端时间估算器。客户端维护一个全局变量EstimatedServerTime,初始值为 EstimatedServerTime = ClientLocalTime + Offset。(Offset = InitialOffset)

读者还记得前面说的第二件事吗,这里就派上用处了。后续收到的回复包中携带的时间戳CurrentServerSendTime, 客户端记录收到回复包的时间ClientReceiveTimeN。客户端会根据这个不断更新估算的时间偏移量得到CurrentOffset,这个更新方法后面再说。然后客户端如果想知道某个回复包对应的服务端状态改变的时间,就可以通过下面的公式计算:

EstimatedServerTimeAtReceive = ClientReceiveTimeN + CurrentOffset

客户端还需要计算观测到的误差 ObservedError。 客户端在ClientReceiveTimeN时,预测此时服务端的时间为EstimatedServerTimeAtReceive = ClientReceiveTimeN + CurrentOffset。 但是根据回复消息里的CurrentServerSendTime以及存在的网络波动可知,实际的服务端时间应该大约是 CurrentServerSendTime + (Estimated One-Way Delay)。但是One-Way Delay New是未知的,因为网络波动具有不确定性,所以客户端通常做一个假设:One-Way Delay New = One Way Delay。

这样观测误差就是:ObservedError = (CurrentServerSendTime + EstimatedOneWayDelay) - (ClientReceiveTimeN + CurrentOffset) ObservedError 就是客户端估算器当前的误差。如果 ObservedError > 0,说明估算器慢了;如果 < 0,说明估算器快了。

得到观测误差后,客户端就可以更新Offset得到CurrentOffset了。客户端不会强暴得将InitialOffset +-= ObservedError,而是会使用一个平滑的方式来更新CurrentOffset。具体的平滑方法可以参考插值处理等数学方法。

到这里,Offset就能够得到持续的更新(在每次收到服务端的回复消息时),客户端如果想知道当前的服务段时间,EstimatedServerTime = getCurrentLocalTime() + Offset。这个变量将作为所有时间敏感的请求的参数,服务端用于进行补偿或作弊检测等等。

EstimatedServerTime的作用是非常大的,比如服务端发送一个时间戳,说游戏在这个时间戳时开始,那么客户端对于这个时间点的判断是用自己的时间还是计算出来的EstimatedServerTime呢?答案是后者。这样就能最大程度的保证所有客户端的时间基准保持一致。

This post is licensed under CC BY 4.0 by the author.