首页 > 代码库 > 网络游戏中的(低精度)时间同步

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

对于网络游戏来说,从物体的移动、攻击到最基础的计时等等,都需要客户端与服务器保持时间的相对一致,那么服务器与客户端同步便是一个必须要解决的问题。通常,网络游戏都会利用心跳来进行同步,那么当客户端并不需要如此精度的同步时,有没有其他方法呢?这里主要讨论低精度的时间同步(精确到秒)。

工作中接触过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.当客户端启动的时候,服务器向客户端下发服务器当前的时间,当客户端需要获取当前时间时,只需用校正过的时间加上本地时间即可。

DWORD TimeSynchronize::GetLocalTime()
{
    return initServerTime  - initClientTime + time(NULL);   ///< initServerTime  - initClientTime为了消除客户端与服务器的时间误差
}
看到这里,细心的同学要笑了,这种做法修改本地的系统时间,不就修改了该函数的返回值么。确实,接口中只要取用了本地时间,便将修改本地时间的风险带入了接口。

2.服务器定时向客户端发送同步消息,就是所谓的心跳机制,类似TCP中的心跳,服务器定时发送一个自定义的结构体(心跳包或心跳帧),让对方知道自己“在线”。 以确保链接的有效性。当服务器超过一定时间没有收到来自客户端的回复,则当做玩家掉线,服务器关闭socket链接。不同的游戏也会根据游戏类型的不同,根据自己的需要设计心跳机制,间隔从几十ms到几s不等。

那么当客户端在大部分时间中并不需要高精度的时间同步时,有没有其他办法以降低对服务器性能的消耗?

3.利用timeGetTime接口,与同步时获取的服务器时间模拟当前时间。

//  timeGetTime:函数以毫秒计的系统时间。该时间为从系统开启算起所经过的时间。 
//  DWORD timeGetTime(VOID); 
//  参数:无参数。 
//  返回值:以毫秒值返回系统时间。
DWORD TimeSynchronize::GetLocalTime()
{
    return initServerTime + timeGetTime() - initClientTimeFromStartup + initServerTime  - initClientTime;
}

可以看到timeGetTime的返回值精度为(ms),对于精度要求为(s)级的应用来说,已然够用了。

当然利用timeGetTime()要注意返回值为32位,取值范围为0~2^32,约为49.71天,要避免溢出。如果服务端利用该接口,又不便于重启服务器,建议利用精度更高的接口,如:QueryPerformanceFrequency(),QueryPerformanceCounter()。


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