网络游戏中的(低精度)时间同步

对于网络游戏来说。从物体的移动、攻击到最基础的计时等等,都须要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()。


原文地址:https://www.cnblogs.com/wzjhoutai/p/7105324.html