hiredis(一个轻量级redis的c客户端)

1.简单介绍

hiredis是一个轻量级的访问redis数据库的c客户端。

它是轻量级的不仅仅是因为它仅仅提供对协议的最小支持,而且它使用了一个高级别的极度类似于printf的api使它的级别远高于其最小代码库和缺乏绑定的redis命令。简而言之,就是更灵活。

除了支持发送命令和接受命令,它还有一个与io层分离的回复解析器。它是一个简单灵活的流解析器,可以用于更高级别的语言绑定以实现有效的回复解析。

 hiredis仅仅支持二进制安全的redsi协议,因此你可以使用它在redis的版本的大于1.2.0.

 hiredis提供多套api,包括同步的api,异步api,回复解析的api.


2.同步api

要使用同步api,仅仅需要学会使用几个函数。

redisContext *redisConnect(const char *ip, int port);      void redisFree(redisContext *c);

void *redisCommand(redisContext *c, const char *format, ...);
void freeReplyObject(void *reply);

1)redisConnect返回一个redisContext的结构体, 这个结构用于保存连接状态。这个结构体包含一个integer类型的err数据成员,这个成员非0,当连接处于一个错误状态的时候,另一个string的数据成员指出具体的错误原因。在调用redisConnect之后应该检查err以测试是否连接成功。

2)向redis发送命令有好几种方式。

第一种方式:

redisCommand,提供了一种类似于printf的形式去发送命令。第一个参数是redisConnect的返回值。

最简单的形式:

reply = redisConnect(context, "set foo bar");

使用%s插入字符串的形式:

reply = redisConnect(context, "SET foo %s", value);

发送二进制字符串的形式,但同时需要指出字符串的长度:

reply = redisCommand(context, "SET foo %b",  (szie_t)valuelen);

发送多个分离的字符串的形式:

reply = redisCommand(context, "set key:%s %s", myid, value);

类似于命令行的形式:

void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

第四个参数是每个参数字符串的长度

回复:

当命令成功执行时,redisCommand的返回值会保留一个回复。发生错误时,返回值为NULL,上下文中的err字段将被设置(请参阅错误部分)。一旦错误返回,上下文不能被重用,你应该建立一个新的连接。
标准回复redisCommand的类型是redisReply。 redisReply中的类型字段应该用于测试收到的回复类型:
REDIS_REPLY_STATUS:
该命令回复了状态回复。状态字符串可以使用reply-> str来访问。该字符串的长度可以使用reply-> len来访问。
REDIS_REPLY_ERROR:
该命令回复了一个错误。错误字符串可以与REDIS_REPLY_STATUS相同访问。
REDIS_REPLY_INTEGER:
该命令用一个整数来回答。整数值可以使用long long类型的reply-> integer字段来访问。
REDIS_REPLY_NIL:
该命令回答了一个零对象。没有要访问的数据。
REDIS_REPLY_STRING:
批量(字符串)回复。答复的值可以使用reply-> str来访问。该字符串的长度可以使用reply-> len来访问。
REDIS_REPLY_ARRAY:

多批量回复。多批量答复中的元素数量存储在reply->元素中。多批量回复中的每个元素也是一个redisReply对象,可以通过reply-> element [.. index ..]进行访问。 Redis可能会回应嵌套数组,但这完全受支持。

应使用freeReplyObject()函数释放回复。 请注意,这个函数将负责释放包含在数组和嵌套数组中的子回复对象,所以用户不需要释放子回复(它实际上是有害的并且会损坏内存)。

3)将命令序列化

当redisCommand系列中的任何函数被调用时,Hiredis首先根据Redis协议格式化该命令。然后将格式化的命令放入redisContext的输出缓冲区中。这个输出缓冲区是动态的,所以它可以容纳任意数量的命令。将命令放入输出缓冲区后,调用redisGetReply。这个函数有以下两个执行路径:
        输入缓冲区非空:
            尝试解析来自输入缓冲区的单个回复并将其返回
            如果没有答复可以解析,相当于输入缓冲区为空的情况
        输入缓冲区为空:
            将整个输出缓冲区写入套接字
            从套接字读取,直到可以解析单个回复

对于序列化命令,唯一需要做的事情是填充输出缓冲区。由于这个原因,除了不返回一个回复之外,可以使用两个与redisCommand系列相同的命令,然后我们用redisGetReply获取命令的返回结果。

    void redisAppendCommand(redisContext *c, const char *format, ...);

    void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

官方文档如上描述,但其实源码中这两个命令的返回值是int。

使用方法如下:

redisReply *reply;
redisAppendCommand(context,"SET foo bar");
redisAppendCommand(context,"GET foo");
redisGetReply(context,&reply); // reply for SET
freeReplyObject(reply);
redisGetReply(context,&reply); // reply for GET

freeReplyObject(reply);

4)error

当函数调用不成功时,根据函数返回NULL或REDIS_ERR。 context中的err字段将为非零值,并设置为以下常量之一:
REDIS_ERR_IO:创建连接时发生I / O错误,尝试写入套接字或从套接字读取。 如果您在应用程序中包含errno.h,则可以使用全局errno变量来找出错误。
REDIS_ERR_EOF:服务器关闭导致空读的连接。
REDIS_ERR_PROTOCOL:解析协议时发生错误。
REDIS_ERR_OTHER:任何其他错误。 目前,只有在指定的主机名连接时才能使用它。
在任何情况下,context中的errstr字段都将设置为保存错误的字符串表示形式

4.代码

编译的时候切记加上动态链接库。可以使用pkg-config --cflags --libs hiredis显示如何加。

/*************************************************************************
 > File Name: main.cpp
 > Author: aben
 > Mail: [email protected] 
> Created Time: Sat 19 May 2018 08:52:02 PM PDT
 ************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <hiredis/hiredis.h>


using namespace std;


void prase(redisReply *reply)
{
int seq = 0;
if (NULL == reply) {
printf("redisReply id NULL\n");
return;
}
switch (reply->type) 
{
case REDIS_REPLY_STATUS:
printf("REDIS_REPLY_STATUS:%s\n", reply->str);
break;
case REDIS_REPLY_ERROR:
printf("REDIS_REPLY_ERROR:%s\n", reply->str);
break;
case REDIS_REPLY_INTEGER:
printf("REDIS_REPLY_INTEGER:%lld\n", reply->integer);
break;
case REDIS_REPLY_NIL:
printf("REDIS_REPLY_NIL:NULL\n");
break;
case REDIS_REPLY_STRING:
printf("REDIS_REPLY_STRING:%s\n", reply->str);
break;
case REDIS_REPLY_ARRAY:
printf("REDIS_REPLY_ARRAY\n");
while(seq < reply->elements) {
printf(" REDIS_REPLY_STRING:%s   ", reply->element[seq]->str);
if (seq++ % 3 == 0) {
printf("\n");
}
}
printf("\n");
break;
default:
break;
}
freeReplyObject(reply);
}


int main()
{
redisContext *c = redisConnect("127.0.0.1", 6379);
if (c == NULL || c->err) {
if (c) {
printf("Error:%s\n", c->errstr);
} else {
printf("conn't allocate redis context\n");
}
exit(-1);
}
printf("redis connect return %d:%s\n", c->err, c->errstr);


  redisReply *reply = (redisReply*)redisCommand(c, "set company noahwm");
prase(reply);
  reply = (redisReply*)redisCommand(c, "set phone 17621079235");
prase(reply);
  reply = (redisReply*)redisCommand(c, "keys *");
prase(reply);




return 0;
}


5.代码 2

修改main函数

int main()
{

redisContext *c = redisConnect("127.0.0.1", 6379);

if (c == NULL || c->err) {
    if (c) {
        printf("Error:%s\n", c->errstr);
    } else {
        printf("conn't allocate redis context\n");
    }
    exit(-1);
}
printf("redis connect success\n");


redisReply *reply = (redisReply*)redisCommand(c, "set company noahwm");
prase(reply);
reply = (redisReply*)redisCommand(c, "set phone 17621079235");
prase(reply);


redisAppendCommand(c, "set family henan");
redisGetReply(c, (void**)&reply); // reply for SET                     
prase(reply);
reply = (redisReply*)redisCommand(c, "keys *");
prase(reply);

return 0;

}

如果使用完append类的command函数之后我们不使用redisGetReply得到回复,那么下一个命令的回复就是上一条命令的回复

猜你喜欢

转载自blog.csdn.net/woaichanganba/article/details/80315720