需求
C++中使用hiredis客户端接口访问redis;
需要使用mset一次设置多个二进制数据
以下给出三种封装实现方案;
简单拼接方案
在redis-cli中,mset的语法是这样的:
/opt/colin$./redis-cli mset a 11 b 22 c 333
OK
按照这样的语法拼接后,直接使用hiredis字符串接口redisCommand传递:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
void msetNotBinary(redisContext *c, const vector<string> &vtKey, const vector<string> & vtVal )
{
if (vtKey.size() != vtVal.size())
{
}
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 **的转换,有一定的风险,
关于这一点前一篇文章已经谈到;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
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中学到的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
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
以上所述就是本文的全部内容了,希望大家能够喜欢。