最新消息:

网游服务器中的GUID(唯一标识码)实现-基于snowflake算法

技术文章 NickYang 11165浏览 17评论

本文中的算法采用twitter的snowflake算法,具体请搜索介绍,原来是用Scala写的,因我项目需要,改写成C++语言,主要用于高效的生成唯一的ID, 核心算法就是毫秒级时间(41位)+机器ID(10位)+毫秒内序列(12位).

网上也有好多PHP写的插件模块,核心用了网络通讯将生成的ID发送给PHP使用,没深入研究PHP的模块写法。

废话不多说了,还是直接上代码好了。

uuid.h

#ifndef __UTIL_UUID_H__
#define __UTIL_UUID_H__

#include <stdint.h>

namespace utils
{
// twitter snowflake算法
// 64       63--------------22---------12---------0
// 符号位   |     41位时间   |10位机器码|12位自增码|
extern uint64_t get_time();

class unique_id_t
{
public:
    unique_id_t();
    ~unique_id_t();

    void set_epoch(uint64_t epoch);
    void set_machine(int32_t machine);
    int64_t generate();

private:
    uint64_t epoch_;
    uint64_t time_;
    int32_t machine_;
    int32_t sequence_;
};

}

#endif // !__UTIL_UUID_H__

uuid.cpp

#include "uuid.h"
#if defined(__GUNC__)
#include <sys/time.h>
#include <unistd.h>
#define EPOCHFILETIME 11644473600000000ULL
#else
#include <windows.h>
#include <time.h>
#define EPOCHFILETIME 11644473600000000Ui64
#endif

namespace utils
{
    uint64_t get_time()
    {
#ifdef __GUNC__
        struct timeval tv;
        gettimeofday(&tv, NULL);
        uint64 time = tv.tv_usec;
        time /= 1000;
        time += (tv.tv_sec * 1000);
        return time;
#else
        FILETIME filetime;
        uint64_t time = 0;
        GetSystemTimeAsFileTime(&filetime);

        time |= filetime.dwHighDateTime;
        time <<= 32;
        time |= filetime.dwLowDateTime;

        time /= 10;
        time -= EPOCHFILETIME;
        return time / 1000;
#endif
    }

    unique_id_t::unique_id_t()
    {
        epoch_ = 0;
        time_ = 0;
        machine_ = 0;
        sequence_ = 0;
    }

    unique_id_t::~unique_id_t()
    {

    }

    void unique_id_t::set_epoch(uint64_t epoch)
    {
        epoch_ = epoch;
    }

    void unique_id_t::set_machine(int32_t machine)
    {
        machine_ = machine;
    }

    int64_t unique_id_t::generate()
    {
        int64_t value = 0;
        uint64_t time = get_time() - epoch_;

        // 保留后41位时间
        value = time << 22;

        // 中间10位是机器ID
        value |= (machine_ & 0x3FF) << 12;

        // 最后12位是sequenceID
        value |= sequence_++ & 0xFFF;
        if (sequence_ == 0x1000)
        {
            sequence_ = 0;
        }

        return value;
    }
}

#ifdef __TEST__
#include <iostream>
void test()
{
    utils::unique_id_t* u_id_ptr = new utils::unique_id_t();
    u_id_ptr->set_epoch(uint64_t(1367505795100));
    u_id_ptr->set_machine(int32_t(100));
    for (int i = 0; i < 1024; ++i)
    {
        std::cout << u_id_ptr->generate() << std::endl;;
    }
}
#endif

这样的唯一ID就可以用来表示你系统中使用的例如物品唯一ID,坐骑唯一ID等等数据,方便记录和追踪。

转载请注明:C++爱好者博客 » 网游服务器中的GUID(唯一标识码)实现-基于snowflake算法

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (17)

  1. time = tv.tv_usec / 1000;//取毫秒time += ((uint64_t)tv.tv_sec * 1000);
    LQF4年前 (2015-02-14)回复
  2. tv.tv_sec * 1000 这个地方加个强转((uint64_t)tv.tv_sec * 1000)就不会溢出了。。
    LQF4年前 (2015-02-14)回复
  3. #include #include #include int main(int argc, char** argv){ printf("%dn", sizeof(time_t)); printf("%dn", sizeof(int64_t)); return 0;}48centos 32位机器
    LQF4年前 (2015-02-14)回复
  4. 用你这个在linux上面跑出现了负数。。
    LQF4年前 (2015-02-14)回复
    • 恩,我知道会有负数的,不过不影响使用,因为C++是移位的,跟原本的snowflake算法中的字符串没法比
      eliteYang4年前 (2015-02-14)回复
  5. linux time_t是long吧。。。。。所以要溢出。。
    LQF4年前 (2015-02-14)回复
  6. time += (tv.tv_sec * 1000); 这个要溢出。
    LQF4年前 (2015-02-13)回复
    • 为善么会溢出,second的位数乘以1000,离64位还远呢
      eliteYang4年前 (2015-02-13)回复
  7. 参见UUID库啊
    dfd5年前 (2014-06-12)回复
  8. 机器ID需要根据每台服务器配置还是自动算的
    penwei5年前 (2014-05-04)回复
    • 可以通过服务器的配置,也可以通过自定义的算法来计算,这个就看自己使用了,只要这个ID能区分开就可以了
      eliteYang5年前 (2014-05-04)回复
  9. utils::unique_id_t* u_id_ptr = new utils::unique_id_t(); 没有delete...指针有这么好么,为毛不用对象
    jerry5年前 (2014-05-04)回复
    • 因为如果在逻辑模块中使用,指针获得和判空比较方便,虽然对象也可以通过&方法取出指针,但总没有指针用指针干净利落。所以,什么时候用指针,什么时候用对象,是要区分环境的,没有说哪个好哪个不好的。
      eliteYang5年前 (2014-05-04)回复
  10. 高级东西呀。
    爱浮夸5年前 (2014-03-08)回复
    • 也不怎么高级,基础内容, 😆
      eliteYang5年前 (2014-03-08)回复