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

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

(0)

相关推荐

  • C++中memset函数用法详解

    本文实例讲述了C++中memset函数用法.分享给大家供大家参考,具体如下: 功 能: 将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作 用 法: void memset(void *s, char ch, unsigned n); 程序示例: #include <string.h> #include <stdio.h> #include <memory.h> int main(v

  • 浅析C++中memset,memcpy,strcpy的区别

    复制代码 代码如下: #include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h> //memcpy:按字节复制原型:extern void* memcpy(void *dest,void *src,unsigned int count)//功能:由src所指内存区域复制count个字节到dest所指的内存区域://同strcpyvoid *memcpy_su(void

  • C++线程优先级SetThreadPriority的使用实例

    本文实例讲述了C++线程优先级SetThreadPriority的使用方法,分享给大家供大家参考.具体方法如下: 复制代码 代码如下: // ThreadPriority.cpp : 定义控制台应用程序的入口点.  //    #include "stdafx.h"  #include <Windows.h>    DWORD WINAPI ThreadProcIdle(LPVOID lpParameter)  {      for (int i=0;i<20;i++

  • c++利用stl set_difference对车辆进出区域进行判定

    核心代码 #include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; struct _AREA_VECTOR_STRUCT { int nAreaType;//区域类型 int nAreaID;//区域ID }; void CtestDlg::OnBnClickedButton2() { vector<_AREA_VE

  • C++ 头文件系列(set)详解

    简介 头文件包含 set . multiset 两个类模版,这里要描述的概念与map非常相似,甚至连成员函数都几乎一样,所以这篇随笔会很短. set set如果翻译成中文应该是集合的意思,这里更确切的说是 唯一有序集合 ,性质与map类似: 关联性 元素唯一性 动态增长 有序性 此外的一个重要特点是: Key与Value是同一个对象(自映射) set == map 定义使用set的时候只需要传入一个类型参数,这个类型即是key,也是value. 实际上, set是map的特殊情况 ,虽然set没

  • C++中关于set删除的一些坑

    前言 最近工作中需要使用平衡树维护操作.遂调用了C++标准库里的set,在确保解题思路没有出错的情况下,我发现自己始终有一组样例无法通过.在检查了很久的细节并找了标程对跑中间过程以后,我发现我在使用set做删除的时候,迭代器发生了和我预想不一样的变化. 我在一个函数中调用了set的erase操作来删除某个迭代器,函数的传入参数为要删除的迭代器,类似如下 set<int> aha; void del(set<int>::iterator it) { //一顿操作 aha.erase(

  • c++ STL set_difference set_intersection set_union 操作

    以下是STL algorithm的几个函数,使用的条件是有序容器,所以 vector在被sort了之后是可以使用的,set也是可以使用的. set_difference 这个是求得在第一个容器中有,第二个容器中没有的.set_intersection 求两个容器的交, set_union 求两个容器的并. set_symmetric_difference 求两个容器的差. 最后使用的时候注意要提前分配好最后的盛放容器,其大小最好是两个操作容器的和,然后需要根据返回的迭代器resize一下,看下面

  • 简单谈谈C++ 头文件系列之(bitset)

    简介 该头文件有关位集,实际上是vector 位 位本质上对应bool的概念,只有0或1,true或false两种对立的值. 但很可惜,字节才是机器上最小的存储单元,所以bool基本上是由一个字节大小. bitset是出于高效的空间利用为目的才出现的. 位操作 operator [] : 通过下标访问bit. count : 计数位值为1的位个数. size : 返回位的大小,即有多少个位. test : 测试下标指向的位值是否为1. any : 判断是否有任何一个位值为1. none : 判断

  • C++标准库bitset类型的简单使用方法介绍

    std::bitset是STL的一部分,准确地说,std::bitset是一个模板类,它的模板参数不是类型,而整形的数值(这一特性是ISO C++2003的新特性),有了它我们可以像使用数组一样使用位. #include<bister> using std::bitset; 一句话定义:可自定义位数,用作记录二进制的数据类型. 一,定义和初始化 bitset<n> b;                           //b有n位,每位都为0; bitset<n>

  • C++ set的使用方法详解

    C++ set的使用方法详解 set也是STL中比较常见的容器.set集合容器实现了红黑树的平衡二叉检索树的数据结构,它会自动调整二叉树的排列,把元素放到适当的位置.set容器所包含的元素的值是唯一的,集合中的元素按一定的顺序排列. 我们构造set集合的目的是为了快速的检索,不可直接去修改键值. set的一些常见操作: begin() 返回指向第一个元素的迭代器 clear() 清除所有元素 count() 返回某个值元素的个数 empty() 如果集合为空,返回true(真) end() 返回

  • C++ STL入门教程(7) multimap、multiset的使用

    一.multimap(一对多索引) C++ multimap和map所支持的操作相同(除了multimap不支持下标运算),但是multimap允许重复的元素. 完整程序代码: /*请务必运行以下程序后对照阅读*/ ///头文件依旧是map #include <map> #include <string> #include <iostream> using namespace std; int main() { ///1. 初始化 multimap<int, st

随机推荐