游戏服务器里GUID的生成算法

这个博客三年多没上了,来一发!!打完这把飞机睡觉去!!凌晨3点了!!!

先提供linux下的模拟:

#include <stdio.h>
#include <stdlib.h>

/**
  本程序可以生成64位全服务器全局唯一ID
  支持每秒4096个注册


  由 时间戳+服务器区号+平台号+本地递增序号 组成


  时间戳 32bit
  服务器区号 12bit
  平台号 8bit
  递增序号 12bit 
*/


const u_int64_t c_mark_time_stamp = 0xffffffff00000000;/*时间戳掩码*/
const u_int64_t c_mark_district = 0x00000000fff00000;/*服务器区号掩码*/
const u_int64_t c_mark_plat = 0x00000000000ff000;/*服务器里的平台号掩码*/
const u_int64_t c_mark_base = 0x0000000000000fff;/*本地ID编号掩码*/


u_int64_t gen_new_guid()
{
    static u_int32_t baseId = 0;
    baseId++;
    u_int64_t timeStamp = (u_int64_t)time(NULL);
    u_int32_t districtNum = 1;/*这个服务器区号可以根据实际情况设置或者获取*/
    u_int32_t platNum = 1;/*这个平台号可以根据实际情况设置或者获取*/
    u_int64_t newId = ((timeStamp << 32)&c_mark_time_stamp) | ((districtNum << 20) & c_mark_district) | ((platNum << 12) & c_mark_plat) | (baseId & c_mark_base);
}


int main()
{
printf("GUID : %llu\n", gen_new_guid());
        exit(0);
}


以下是在windows下对该算法的正确性测试与分析:


#include "stdafx.h"
#include <time.h>
#include <stdlib.h>
#include <dos.h>
#include <Windows.h>
#include <iostream>
#include <vector>
#include <map>


typedef unsigned __int64 u_int64_t;
typedef unsigned int u_int32_t;


const u_int64_t c_mark_time_stamp = 0xffffffff00000000;/*时间戳掩码*/
const u_int64_t c_mark_district = 0x00000000fff00000;/*服务器区号掩码*/
const u_int64_t c_mark_plat = 0x00000000000ff000;/*服务器里的平台号掩码*/
const u_int64_t c_mark_base = 0x0000000000000fff;/*本地ID编号掩码*/


//生成所有游戏服务器里全局唯一的ID,可防范于各种合服时ID相同时难处理的问题
u_int64_t GenNewGUID()
{
static u_int32_t baseId = 0;
baseId++;


u_int64_t timeStamp = (u_int64_t)time(NULL);
u_int32_t districtNum = 1;/*这个服务器区号可以根据实际情况设置或者获取*/
u_int32_t platNum = 1;/*这个平台号可以根据实际情况设置或者获取*/
u_int64_t newId = ((timeStamp << 32)&c_mark_time_stamp) | ((districtNum << 20) & c_mark_district) | ((platNum << 12) & c_mark_plat) | (baseId & c_mark_base);


return newId;
}


int _tmain(int argc, _TCHAR* argv[])
{
std::vector<u_int64_t> idList;
std::map<u_int64_t, u_int64_t> idMap;


DWORD dwLastTime = ::GetTickCount();
DWORD dwCurTime = 0;
u_int64_t uMaxIdOneSec = 4096; // 此算法1秒内支持产生uMaxIdOneSec个GUID,超过此值就会出现重复值
u_int64_t uSecondCnt = 0;//统计运行了多少秒
u_int64_t uSecondOver = 2; // 产生uSecondOver秒后停止,此时将会产生 uMaxIdOneSec * uSecondOver 个 GUID

while (true)
{
dwCurTime = ::GetTickCount();
if (dwCurTime - dwLastTime >= 1000)
{
dwLastTime = dwCurTime;
uSecondCnt++;
if (uSecondCnt >= uSecondOver) break;
}


if (idList.size() < uMaxIdOneSec * (uSecondCnt + 1))
{
idList.push_back(GenNewGUID());
}

}


for (std::vector<u_int64_t>::iterator it = idList.begin(); it != idList.end(); it++)
{
idMap[*it] = 0;
}
if (idList.size() > idMap.size())
{
//两个size不相等说明有重复的Id,证明算法失败! 请检查uMaxIdOneSec是否是4096!!
MessageBox(NULL, L"have same id !!!", L"title", MB_OK);
}


/**
测试监视情况:


第1秒结束时第4096个GUID是:234636408433676288 
第2秒开始时第4097个GUID是:234636412728643585 

分别对应的二进制是:

00000011010000011001100010010010 000000000001 00000001 000000000000

00000011010000011001100010010011 000000000001 00000001 000000000001


因为时间戳c_mark_time_stamp的前32位是掩码,所以根据以上数据可以看出在进行第2秒开始产生新的GUID时高32位的时间戳加了1
但是最后12位的本地ID编号也递增了1*/


return 0;
}

 

以下是C#版本:

 

class Program
    {
        public static ulong c_mark_time_stamp = 0xffffffff0000000;/*时间戳掩码*/
        public static ulong c_mark_district = 0x00000000fff00000;/*服务器区号掩码*/
        public static ulong c_mark_plat = 0x00000000000ff000;/*服务器里的平台号掩码*/
        public static ulong c_mark_base = 0x0000000000000fff;/*本地ID编号掩码*/
        private static uint c_baseId = 0;/*本地ID*/

        //生成所有游戏服务器里全局唯一的ID,可防范于各种合服时ID相同时难处理的问题
        public static ulong GenNewGUID()
        {
            ulong timeStamp = (ulong)(DateTime.Now - TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1))).TotalSeconds;//获取自古以来的时间戳
            uint districtNum = 1;/*这个服务器区号可以根据实际情况设置或者获取*/
            uint platNum = 1;/*这个平台号可以根据实际情况设置或者获取*/
            ulong newId = ((timeStamp << 32) & c_mark_time_stamp) | ((districtNum << 20) & c_mark_district) | ((platNum << 12) & c_mark_plat) | (c_baseId & c_mark_base);
            c_baseId++;

            return newId;
        }

        static void Main(string[] args)
        {
            List<ulong> idList = new List<ulong>();
            Dictionary<ulong, ulong> idMap = new Dictionary<ulong, ulong>();          

            int lastTime = System.Environment.TickCount;
            int curTime = 0;
            ulong uMaxIdOneSec = 4096; // 此算法1秒内支持产生uMaxIdOneSec个GUID,超过此值就会出现重复值
            ulong uSecondCnt = 0;//统计运行了多少秒
            ulong uSecondOver = 2; // 产生uSecondOver秒后停止,此时将会产生 uMaxIdOneSec * uSecondOver 个 GUID

            while (true)
            {
                curTime = System.Environment.TickCount;
                if (curTime - lastTime >= 1000)
                {
                    lastTime = curTime;
                    uSecondCnt++;
                    if (uSecondCnt >= uSecondOver)
                    {
                        break;
                    }
                }

                if ((ulong)idList.Count < uMaxIdOneSec * (uSecondCnt + 1))
                {
                    ulong val = GenNewGUID();
                    idList.Add(val);
                }
            }

            for (int index = 0; index < idList.Count; index++)
            {
                idMap[idList[index]] = 0;
            }
            if (idList.Count > idMap.Count)
            {
                while(true)
                {
                    Console.WriteLine("两个Count不相等说明有重复的Id,证明算法失败! 请检查uMaxIdOneSec是否是4096!!");
                }
            }

            return;
        }

    }


 



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值