这个博客三年多没上了,来一发!!打完这把飞机睡觉去!!凌晨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;
}
}