程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++ Redis mset 二進制數據接口封裝方案,redismset

C++ Redis mset 二進制數據接口封裝方案,redismset

編輯:C++入門知識

C++ Redis mset 二進制數據接口封裝方案,redismset


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]) );
    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
"AB\x00DE"

完整可執行的代碼詳見github:https://github.com/me115/cppset/tree/master/2DimArray

參考資料

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

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved