资讯专栏INFORMATION COLUMN

C++ Redis mset 二进制数据接口封装方案

rubyshen / 2315人阅读

摘要:二进制数据接口封装方案需求中使用客户端接口访问需要使用一次设置多个二进制数据以下给出三种封装实现方案简单拼接方案在中,的语法是这样的按照这样的语法拼接后,直接使用字符串接口传递这种方式可以处理多个字符串数据,但对于数据内容为二进制

C++ Redis mset 二进制数据接口封装方案 需求

C++中使用hiredis客户端接口访问redis;
需要使用mset一次设置多个二进制数据

以下给出三种封装实现方案;

简单拼接方案

在redis-cli中,mset的语法是这样的:

/opt/colin$./redis-cli mset a 11 b 22 c 333
OK

按照这样的语法拼接后,直接使用hiredis字符串接口redisCommand传递:

void msetNotBinary(redisContext *c, const vector &vtKey, const vector & vtVal )
{
    if(vtKey.size() != vtVal.size())
    {
        throw runtime_error( "Redis error" );
    }

    string strCmd = "MSET";
    for(int i = 0; i < vtKey.size(); i++)
    {
        strCmd += " "+vtKey[i]+" "+vtVal[i];
    }
    cout << "strCmd:" << strCmd << endl;

    void * r =  redisCommand(c, strCmd.c_str() );
    if ( !r )
        throw runtime_error( "Redis error" );
    freeReplyObject( r );
}

void do_test( redisContext *c )
{   
    vector vtKey;
    vector vtVal;

    vtKey.push_back("A");
    vtVal.push_back("AAAA");
    vtKey.push_back("B");
    vtVal.push_back("BBBB");
    vtKey.push_back("C");
    vtVal.push_back("CCCC");
    //add a binary data
    vtKey.push_back("D");
    vtVal.push_back("");
    char a[] = "ABCDE";
    a[2] = 0;
    vtVal[3].assign(a,5);

    try
    {
        msetNotBinary(c, vtKey, vtVal );
        //mset1( c, vtKey, vtVal );
        //mset2( c, vtKey, vtVal );
    }
    catch ( runtime_error & )
    {
        cout << "Error" << endl;
    }
}

int main(int argc, char *argv[])
{
    redisContext *c;

    c = redisConnect("127.0.0.1",6379);
    if (c->err)
    {
        cout << "Connection error: " << c->errstr << endl;
        return -1;
    }

    do_test(c);

    redisFree(c);

    return 0;
}

这种方式可以处理mset多个字符串数据,但对于数据内容为二进制数据的无能为力;

redisCommandArgv接口传递 方案

对于多个参数传递,hiredis提供了以下接口,这个接口中最后一个参数是所有的传入数据的内容长度,
就是说这个接口是二进制安全的:

void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

主要工作就是构造一个动态的二维数组char ** argv,其中涉及到char * * 到const char * *的转换,有一定的风险,
关于这一点前一篇文章已经谈到;

void mset1( redisContext *c, const vector &vtKey, const vector & vtVal )
{
    if(vtKey.size() != vtVal.size())
    {
        throw runtime_error( "Redis error" );
    }

    char ** argv = new char*[vtKey.size() + vtVal.size() + 1 ];
    size_t * argvlen = new size_t[vtKey.size() + vtVal.size() + 1 ];

    int j = 0;
    argv[j] = new char[5];
    memcpy(argv[j],"MSET",4);
    argvlen[j] = 4;
    ++j;


    for(int i = 0 ; i < vtKey.size();i++)
    {   
        argvlen[j] = vtKey[i].length();
        argv[j] = new char[argvlen[j]];
        memset((void*)argv[j],0,argvlen[j] );
        memcpy((void*)argv[j],vtKey[i].data(),vtKey[i].length());
        j++;

        argvlen[j] = vtVal[i].length();
        argv[j] = new char[argvlen[j]];
        memset((void*)argv[j],0,argvlen[j]);
        memcpy((void*)argv[j],vtVal[i].data(),vtVal[i].length());
        j++;
    }

    //if not use const_cast ,compile error
    //for why assign from char** to const char** error, see my blog ...
    void *r = redisCommandArgv(c, vtKey.size() + vtVal.size() + 1, const_cast(argv), argvlen );
    if ( !r )
        throw runtime_error( "Redis error" );
    freeReplyObject( r );

    for(int i = 0;i < vtKey.size();i++)
    {
        delete [] argv[i];
        argv[i] = NULL;
    }

    delete []argv;
    delete []argvlen;
    argv = NULL;
}
redisCommandArgv接口传递的Vector方案

还是使用redisCommandArgv接口,使用vector来构造这个const char **,这个方法是从参考资料1中学到的:

void mset2( redisContext *c, const vector &vtKey, const vector & vtVal)
{
    if(vtKey.size() != vtVal.size())
    {
        throw runtime_error( "Redis error" );
    }

    vector argv( vtKey.size() + vtVal.size() + 1 );
    vector argvlen( vtKey.size() +  vtVal.size() + 1 );
    int j = 0;

    static char msetcmd[] = "MSET";
    argv[j] = msetcmd;
    argvlen[j] = sizeof(msetcmd)-1;
    ++j;

    for(int i = 0;i< vtKey.size();++i)
    {
        argvlen[j] = vtKey[i].length();
        argv[j] = new char[argvlen[j]];
        memset((void*)argv[j],0,argvlen[j] );
        memcpy((void*)argv[j],vtKey[i].data(),vtKey[i].length());
        j++;

        argvlen[j] = vtVal[i].length();
        argv[j] = new char[argvlen[j]];
        memset((void*)argv[j],0,argvlen[j]);
        memcpy((void*)argv[j],vtVal[i].data(),vtVal[i].length());
        j++;
    }

    void *r = redisCommandArgv(c, argv.size(), &(argv[0]), &(argvlen[0]) );
    if ( !r )
        throw runtime_error( "Redis error" );
    freeReplyObject( r );
}

这样,就实现二进制数据的传递;

二进制校验

程序执行后,可以用redis-cli来验证:

对于非二进制安全的实现,二进制内容是截断的:
/opt/app/colin$./redis-cli get D
"AB"
而二进制安全的实现接口,二进制数据的0通过转义方式显示:
/opt/app/colin$./redis-cli get D
"ABx00DE"

完整可执行的代码详见github:https://github.com/me115/cppset/tree/master/2DimArray

参考资料

https://gist.github.com/dspezia/1455082

Posted by: 大CC | 8JAN,2015
博客:blog.me115.com [订阅]
微博:新浪微博

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/36085.html

相关文章

  • 学会这15点,让你分分钟拿下 Redis 数据

    摘要:它通常被称为数据结构服务器,因为值可以是字符串哈希列表集合和有序集合等类型。表示内核允许分配所有的物理内存,而不管当前的内存状态如何。管理方便,后续可自行增加或摘除节点,移动分槽等等。 本文已获得原作者霸都民工哥授权。 1、Redis 简介 REmote DIctionary Server(Redis)  是一个由 Salvatore Sanfilippo写的 key-value 存储...

    SKYZACK 评论0 收藏0
  • 学会这15点,让你分分钟拿下 Redis 数据

    摘要:它通常被称为数据结构服务器,因为值可以是字符串哈希列表集合和有序集合等类型。表示内核允许分配所有的物理内存,而不管当前的内存状态如何。管理方便,后续可自行增加或摘除节点,移动分槽等等。 本文已获得原作者霸都民工哥授权。 1、Redis 简介 REmote DIctionary Server(Redis)  是一个由 Salvatore Sanfilippo写的 key-value 存储...

    jhhfft 评论0 收藏0
  • 学会这15点,让你分分钟拿下 Redis 数据

    摘要:它通常被称为数据结构服务器,因为值可以是字符串哈希列表集合和有序集合等类型。表示内核允许分配所有的物理内存,而不管当前的内存状态如何。管理方便,后续可自行增加或摘除节点,移动分槽等等。 本文已获得原作者霸都民工哥授权。 1、Redis 简介 REmote DIctionary Server(Redis)  是一个由 Salvatore Sanfilippo写的 key-value 存储...

    zhangke3016 评论0 收藏0
  • Redis 常用操作命令,非常详细!

    摘要:下面总结并演示了的常用管理命令操作字符串集合列表散列类型的操作命令。常用管理命令启动如果命令参数过多,建议通过配置文件来启动。同样,递减使用命令。 下面总结并演示了 Redis 的 常用管理命令、key 操作、字符串、集合、列表、散列类型的操作命令。 你需要掌握的 Redis 知识 史上最全 Redis 高可用解决方案总结 为什么分布式一定要有Redis? Spring Boot R...

    fevin 评论0 收藏0
  • lua web快速开发指南(6) - Cache、DB介绍

    摘要:数据库与缓存的基本概念数据库与缓存是服务端开发人员的必学知识点数据库数据库是一种信息记录存取的虚拟标记地点的集合统称比如现实生活中我们经常会用到文件柜书桌等等数据存取容器在对容器进行数据存取的时候我们会为每一层打上一个标签表示一种分类项而这 数据库与缓存的基本概念 数据库与缓存是服务端开发人员的必学知识点. 数据库 数据库是一种信息记录、存取的虚拟标记地点的集合统称. 比如现实生活中,...

    daryl 评论0 收藏0

发表评论

0条评论

rubyshen

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<