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


    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<string> &vtKey, const vector<string> & 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<string> vtKey;
        vector<string> 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<string> &vtKey, const vector<string> & 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<const char**> ,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<const char**>(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<string> &vtKey, const vector<string> & vtVal)
    {
        if(vtKey.size() != vtVal.size())
        {
            throw runtime_error( "Redis error" );
        }
    
        vector<const char *> argv( vtKey.size() + vtVal.size() + 1 );
        vector<size_t> 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]) );
      
      for(int i = 0; i < argv.size();i++)
      {
        delete argv[i];
        argv[i] = NULL;
      }
    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 [订阅]
    微博:新浪微博

  • 相关阅读:
    TCP/IP协议栈之QEMU
    FreeRTOS-Plus-CLI中添加一个自己的命令行
    FreeRTOS A57
    log日志库
    函数解读:ioremap / ioremap_nocache / ioremap_wc / ioremap_wt
    Makefile 使用小结
    41. 缺失的第一个正数(First Missing Positive)
    42. 接雨水(Trapping Rain Water)
    关于C++内联和静态成员函数的问题
    C++11 线程并发问题
  • 原文地址:https://www.cnblogs.com/me115/p/4279876.html
Copyright © 2020-2023  润新知