网络游戏中的(低精度)时间同步
对于网络游戏来说。从物体的移动、攻击到最基础的计时等等,都须要client与server保持时间的相对一致,那么server与client同步便是一个必须要解决的问题。通常,网络游戏都会利用心跳来进行同步。那么当client并不须要如此精度的同步时,有没有其它方法呢?这里主要讨论低精度的时间同步(精确到秒)。
工作中接触过3种简单的时间同步方法:
首先,定义时间同步类
/// 32位操作系统 typedef unsigned int64_t QWORD; typedef unsigned long DWORD; class TimeSynchronize() { public: void ServerTimeSync(QWORD ServerTime); QWORD GetLocalTime(); private: QWORD initServerTime; QWORD initClientTime; DWORD initClientTimeFromStartup; }; /// 服务器下发同步消息 void TimeSynchronize::ServerTimeSync(unsigned int64_t serverTime) { initClientTime = time(NULL); initServerTime = serverTime; initClientTimeFromStartup = timeGetTime(); }
1.当client启动的时候。server向client下发server当前的时间。当client须要获取当前时间时。仅仅需用校正过的时间加上本地时间就可以。
DWORD TimeSynchronize::GetLocalTime() { return initServerTime - initClientTime + time(NULL); ///< initServerTime - initClientTime为了消除client与server的时间误差 }看到这里,细心的同学要笑了,这样的做法改动本地的系统时间,不就改动了该函数的返回值么。确实,接口中仅仅要取用了本地时间。便将改动本地时间的风险带入了接口。
2.server定时向client发送同步消息,就是所谓的心跳机制。类似TCP中的心跳,server定时发送一个自己定义的结构体(心跳包或心跳帧),让对方知道自己“在线”。
以确保链接的有效性。当server超过一定时间没有收到来自client的回复。则当做玩家掉线,server关闭socket链接。不同的游戏也会依据游戏类型的不同,依据自己的须要设计心跳机制,间隔从几十ms到几s不等。
那么当client在大部分时间中并不须要高精度的时间同步时,有没有其它办法以减少对server性能的消耗?
3.利用timeGetTime接口。与同步时获取的server时间模拟当前时间。
// timeGetTime:函数以毫秒计的系统时间。该时间为从系统开启算起所经过的时间。 // DWORD timeGetTime(VOID); // 參数:无參数。// 返回值:以毫秒值返回系统时间。 DWORD TimeSynchronize::GetLocalTime() { return initServerTime + timeGetTime() - initClientTimeFromStartup + initServerTime - initClientTime; }
能够看到timeGetTime的返回值精度为(ms),对于精度要求为(s)级的应用来说,已然够用了。
当然利用timeGetTime()要注意返回值为32位,取值范围为0~2^32,约为49.71天,要避免溢出。
假设服务端利用该接口,又不便于重新启动server,建议利用精度更高的接口,如:QueryPerformanceFrequency(),QueryPerformanceCounter()。