首页 > 编程 > C++ > 正文

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

2020-05-23 14:19:23
字体:大 中 小
来源:转载
供稿:网友
这篇文章主要介绍了C++访问Redis的mset 二进制数据接口封装方案的相关资料,需要的朋友可以参考下
 

需求

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

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

简单拼接方案

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

 

复制代码代码如下:

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

 

OK

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

  1. void msetNotBinary(redisContext *c, const vector<string> &vtKey, const vector<string> & vtVal ) 
  2. { 
  3.   if(vtKey.size() != vtVal.size()) 
  4.   { 
  5.     throw runtime_error( "Redis error" ); 
  6.   } 
  7.  
  8.   string strCmd = "MSET"; 
  9.   for(int i = 0; i < vtKey.size(); i++) 
  10.   { 
  11.     strCmd += " "+vtKey[i]+" "+vtVal[i]; 
  12.   } 
  13.   cout << "strCmd:" << strCmd << endl; 
  14.  
  15.   void * r = redisCommand(c, strCmd.c_str() ); 
  16.   if ( !r ) 
  17.     throw runtime_error( "Redis error" ); 
  18.   freeReplyObject( r ); 
  19. } 
  20.  
  21. void do_test( redisContext *c ) 
  22. {   
  23.   vector<string> vtKey; 
  24.   vector<string> vtVal; 
  25.  
  26.   vtKey.push_back("A"); 
  27.   vtVal.push_back("AAAA"); 
  28.   vtKey.push_back("B"); 
  29.   vtVal.push_back("BBBB"); 
  30.   vtKey.push_back("C"); 
  31.   vtVal.push_back("CCCC"); 
  32.   //add a binary data 
  33.   vtKey.push_back("D"); 
  34.   vtVal.push_back(""); 
  35.   char a[] = "ABCDE"; 
  36.   a[2] = 0; 
  37.   vtVal[3].assign(a,5); 
  38.  
  39.   try 
  40.   { 
  41.     msetNotBinary(c, vtKey, vtVal ); 
  42.     //mset1( c, vtKey, vtVal ); 
  43.     //mset2( c, vtKey, vtVal ); 
  44.   } 
  45.   catch ( runtime_error & ) 
  46.   { 
  47.     cout << "Error" << endl; 
  48.   } 
  49. } 
  50.  
  51. int main(int argc, char *argv[]) 
  52. { 
  53.   redisContext *c; 
  54.  
  55.   c = redisConnect("127.0.0.1",6379); 
  56.   if (c->err) 
  57.    { 
  58.     cout << "Connection error: " << c->errstr << endl; 
  59.     return -1; 
  60.   } 
  61.  
  62.   do_test(c); 
  63.  
  64.   redisFree(c); 
  65.  
  66.   return 0; 
  67. } 
?

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

redisCommandArgv接口传递 方案

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

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

  1. void mset1( redisContext *c, const vector<string> &vtKey, const vector<string> & vtVal ) 
  2. { 
  3.   if(vtKey.size() != vtVal.size()) 
  4.   { 
  5.     throw runtime_error( "Redis error" ); 
  6.   } 
  7.  
  8.   char ** argv = new char*[vtKey.size() + vtVal.size() + 1 ]; 
  9.   size_t * argvlen = new size_t[vtKey.size() + vtVal.size() + 1 ]; 
  10.  
  11.   int j = 0; 
  12.   argv[j] = new char[5]; 
  13.   memcpy(argv[j],"MSET",4); 
  14.   argvlen[j] = 4; 
  15.   ++j; 
  16.  
  17.  
  18.   for(int i = 0 ; i < vtKey.size();i++) 
  19.   {   
  20.     argvlen[j] = vtKey[i].length(); 
  21.     argv[j] = new char[argvlen[j]]; 
  22.      memset((void*)argv[j],0,argvlen[j] ); 
  23.     memcpy((void*)argv[j],vtKey[i].data(),vtKey[i].length()); 
  24.     j++; 
  25.  
  26.     argvlen[j] = vtVal[i].length(); 
  27.     argv[j] = new char[argvlen[j]]; 
  28.     memset((void*)argv[j],0,argvlen[j]); 
  29.     memcpy((void*)argv[j],vtVal[i].data(),vtVal[i].length()); 
  30.     j++; 
  31.   } 
  32.  
  33.   //if not use const_cast<const char**> ,compile error 
  34.   //for why assign from char** to const char** error, see my blog ... 
  35.    void *r = redisCommandArgv(c, vtKey.size() + vtVal.size() + 1, const_cast<const char**>(argv), argvlen ); 
  36.   if ( !r ) 
  37.     throw runtime_error( "Redis error" ); 
  38.   freeReplyObject( r ); 
  39.  
  40.   for(int i = 0;i < vtKey.size();i++) 
  41.   { 
  42.     delete [] argv[i]; 
  43.     argv[i] = NULL; 
  44.   } 
  45.  
  46.   delete []argv; 
  47.   delete []argvlen; 
  48.   argv = NULL; 
  49. } 
?

redisCommandArgv接口传递的Vector方案

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

  1. void mset2( redisContext *c, const vector<string> &vtKey, const vector<string> & vtVal) 
  2. { 
  3.   if(vtKey.size() != vtVal.size()) 
  4.   { 
  5.     throw runtime_error( "Redis error" ); 
  6.   } 
  7.  
  8.   vector<const char *> argv( vtKey.size() + vtVal.size() + 1 ); 
  9.   vector<size_t> argvlen( vtKey.size() + vtVal.size() + 1 ); 
  10.   int j = 0; 
  11.  
  12.   static char msetcmd[] = "MSET"; 
  13.   argv[j] = msetcmd; 
  14.   argvlen[j] = sizeof(msetcmd)-1; 
  15.   ++j; 
  16.  
  17.   for(int i = 0;i< vtKey.size();++i) 
  18.   { 
  19.     argvlen[j] = vtKey[i].length(); 
  20.     argv[j] = new char[argvlen[j]]; 
  21.      memset((void*)argv[j],0,argvlen[j] ); 
  22.     memcpy((void*)argv[j],vtKey[i].data(),vtKey[i].length()); 
  23.     j++; 
  24.  
  25.     argvlen[j] = vtVal[i].length(); 
  26.     argv[j] = new char[argvlen[j]]; 
  27.     memset((void*)argv[j],0,argvlen[j]); 
  28.     memcpy((void*)argv[j],vtVal[i].data(),vtVal[i].length()); 
  29.     j++; 
  30.   } 
  31.  
  32.   void *r = redisCommandArgv(c, argv.size(), &(argv[0]), &(argvlen[0]) ); 
  33.   if ( !r ) 
  34.     throw runtime_error( "Redis error" ); 
  35.   freeReplyObject( r ); 
  36. } 
?

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

二进制校验

程序执行后,可以用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

以上所述就是本文的全部内容了,希望大家能够喜欢。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表