利用MD5摘要生成guid的方法

kipway@outlook.com

2017.1.26

GUID(Globally Unique Identifier),全局唯一标识符,是一种由算法生成,无需认证机构管理的二进制长度为128位的数字标识符,微软在Windows系统的注册表中用来标识COM对象的类厂和接口,是UUID的具体实现。在软件编程中,常将GUID当作索引使用。

在windows系统中,有平台sdk函数CoCreateGuid可以直接调用,在Linux系统中无相应的系统调用,但有第三方库可用。本文给出一个windows/linux平台均可用的C++实现的GUID计算方法。

guid本质上是一个16字节的数据,按照如下结构体组织:


    struct t_guid
    {
        unsigned int   v1;
        unsigned short v2;
        unsigned short v3;
        unsigned char  v4[8];
    };
             

其实这16字节如何组织没有关系,主要目的要保证计算出来的这16字节尽量随机离散。MD5校验值,也叫MD5摘要值正好是128位共16字节长度,不同源数据的MD5摘要 值重复的概率完全满足GUID重复概率的要求。而且MD5算法的离散型非常好,源数据只要有一位的不同生成的MD5摘要差别很大,因此只要保证源数据能不同就可使用MD5算法计算出满足要求的GUID。

这里源数据采用以下4部分组成,当然你还可以增加一些字段,比如你的有效邮箱地址和你的手机号,进一步保证生成的guid是全球唯一的:

可以按照如下结构体定义:

                         
#ifdef _WIN32
        struct t_guidinfo
        {
            FILETIME	    ts;     // 时标,8字节
            unsigned int    pid;    // 进程pid
            unsigned int    seqno;  // 进程内递增序列号
            unsigned char   mac[8]; // 网卡mac地址
        } _uinfo;
#else
        struct t_guidinfo
        {
            timespec        ts;     // 时标,16字节
            unsigned int    pid;    // 进程pid
            unsigned int    seqno;  // 进程内递增序列号
            unsigned char   mac[8]; // 网卡mac地址
        } _uinfo;
#endif
             

接下来就是获取网卡MAC地址,获取时间,计算MD5摘要等,均可从查资料得到,下面给出完整的代码以便理解:

                         
/*!
\file c_guid.h
\author	 kipway@outlook.com
*/
#ifndef C_GUID_H
#define C_GUID_H
#include <time.h>
#include "c_netmac.h"
#include "c_md5.h"
#ifdef _WIN32
#include <process.h>
#endif
namespace ec
{
    struct t_guid
    {
        unsigned int   v1;
        unsigned short v2;
        unsigned short v3;
        unsigned char  v4[8];
    };
    class cGuid
    {
    protected:
#ifdef _WIN32
        struct t_guidinfo
        {
            FILETIME	    ts;     // 时标,8字节
            unsigned int    pid;    // 进程pid
            unsigned int    seqno;  // 进程内递增序列号
            unsigned char   mac[8]; // 网卡mac地址
        } _uinfo;
#else
        struct t_guidinfo
        {
            timespec        ts;     // 时标,16字节
            unsigned int    pid;    // 进程pid
            unsigned int    seqno;   // 进程内递增序列号
            unsigned char   mac[8]; // 网卡mac地址
        } _uinfo;
#endif
    public:
        cGuid() {
            memset(&_uinfo, 0, sizeof(_uinfo));
            if (!ec::getnetmac(_uinfo.mac, 1))
            {
                size_t i;
                for (i = 0; i < sizeof(_uinfo.mac); i++)
                    _uinfo.mac[i] = (unsigned char)(0xC1 + i);
            }
            _uinfo.pid = getpid();
            _uinfo.seqno = 1;
        }
        void uuid(t_guid *pguid)
        {
#ifdef _WIN32			
            GetSystemTimeAsFileTime(&_uinfo.ts);
#else
            clock_gettime(CLOCK_REALTIME, &_uinfo.ts);
#endif
            _uinfo.seqno++;
            ec::encode_md5(&_uinfo, sizeof(_uinfo), (unsigned char*)pguid);
        }
    };
}
#endif
             

具体测试和使用例子:


/*!
\file tstguid.cpp
*/
#include "ec/c_system.h"
#include "ec/c_guid.h"
ec::cGuid g_clsguid;//全局对象保证进程内guid唯一。
void printfguid(ec::t_guid * p)
{
    printf("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n", p->v1, p->v2, p->v3, p->v4[0], p->v4[1],
        p->v4[2], p->v4[3], p->v4[4], p->v4[5], p->v4[6], p->v4[7]);
}
int main()
{
    ec::t_guid guids[16];
    int i;
    for (i = 0; i < 16; i++)
    {
        g_clsguid.uuid(&guids[i]);
        printfguid(&guids[i]);
    }
    return 0;
}
             

这是在我的机器上例子输出的结果:


            {8D030A83-0AE2-3990-3EA5-33CC66ED794E}
            {1B3A78FE-CC00-BE9D-2D0A-407865D9D656}
            {7ED49EAA-5290-5BE6-A47B-5206F6098E5C}
            {847C56AF-7640-322A-0415-E2BDB4682393}
            {98EE22BC-94B2-1406-C7E4-31CEAE09F446}
            {0010977D-B657-A926-3AB6-4C6F44A91760}
            {3051A369-AC9B-49FD-44EE-94AFEEC9E173}
            {2E565A1B-56DF-FD16-EF9C-B77B8B004B76}
            {89F55B89-F7EE-FF69-4297-F46663A42A31}
            {5A5CC12E-0D6A-D7F5-7688-5FF9E4836502}
            {D3BC7DFA-83F6-34B0-7B64-8093365B7D6E}
            {2C2727C9-83D1-E081-8B0B-A59808FEFF7D}
            {C49AE6DB-72E5-E7A9-4A73-0CBEB91E15F3}
            {69E931AD-0B2D-2CBA-3520-8C20EA0E8FFC}
            {4F57BB42-D484-EAC0-90CD-B26CB60B7FF7}
            {FC302D9D-5472-33D8-AE3E-4A5C96BA5A54}