hiredis(Synchronous API)

hiredis是一个小型的client端的c库。它只增加了最小对协议的支持,同时它用一个高级别的printf-alike API为了绑定各种redis命令。除了支持发送和接收命令,它还支持对流的解析。hiredis仅支持binary-safe的redis协议,所以需要用的redis版本>=1.2.0. 这个库包括多个API, 包括同步API,异步API和返回的解析API等。

安装hiredis:

1)编译完redis后,在/users/denver/rsun/test/redis/redis-2.6.10/deps/hiredis目录下会生成相应的动态链接库libhiredis.so和静态链接库libhiredis.a,

2)执行make install

会将头文件和库文件copy到指定的目录中:

dcmvrh12% make install

mkdir -p /u1/rsun/test/redis/redis-2.6.10/include/hiredis /u1/rsun/test/redis/redis-2.6.10/lib

cp -a hiredis.h async.h adapters /u1/rsun/test/redis/redis-2.6.10/include/hiredis

cp -a libhiredis.so /u1/rsun/test/redis/redis-2.6.10/lib/libhiredis.so.0.10

cd /u1/rsun/test/redis/redis-2.6.10/lib && ln -sf libhiredis.so.0.10 libhiredis.so.0

cd /u1/rsun/test/redis/redis-2.6.10/lib && ln -sf libhiredis.so.0 libhiredis.so

cp -a libhiredis.a /u1/rsun/test/redis/redis-2.6.10/lib

安装完就可以使用了。

 

关于hiredis支持的Synchronous API,主要包括以下函数:

redisContext *redisConnect(const char *ip, int port);
void *redisCommand(redisContext *c, const char *format, ...);
void freeReplyObject(void *reply);

1 连接

redisConnect函数用来生成redisContext。该上下文用来存储connect状态。redisContext结构有一个整形err字段(非0值)用来保存连接的错误状态。字段errstr用来保存错误描述。当通过redisConnect连接redis结束后,可以check err字段来查看连接是否成功。

redisContext *c = redisConnect("127.0.0.1", 6379);
if (c != NULL && c->err) {
    printf("Error: %s\n", c->errstr);
    //handle error
}

1) redisContext结构体存储中存储错误信息,文件描述符fd,写缓冲区,redisReader类对象指针。

typedef struct redisContext {
    int err;
    char errstr[128];
    int fd;
    int flags;
    char *obuf;
    redisReader *reader;
} redisContext;

2) redisConnect函数实现连接到一个redis server上。

redisContext *redisConnect(const char *ip, int port) {
    redisContext *c = redisContextInit();
    c->flags |= REDIS_BLOCK; //设置连接类型是阻塞的
    redisContextConnectTcp(c, ip, port, NULL);
    return c;
}

    a. redisConnect函数用于初始化redisContext结构体指针。

    b.redisContextConnectTcp函数的定义位于net.c中

        根据输入的ip和port绑定地址,使用TCP连接,通过getaddrinfo函数;

        创建socket;

        创建socket属性信息(阻塞),通过fcntl函数;

        连接redis server端,通过connect函数;

        设置socket属性信息,通过setsockopt函数;

2 发送命令

  有许多方式来发送命令给redis。redisCommand方法和printf相似:

reply = redisCommand(context, "SET foo bar");

可以用%s来表示string类型。

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

当需要发送二进制安全的string类型时,需要使用%b,后面跟string类型以及他的长度。

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

redisCommand函数实现:

void *redisCommand(redisContext *c, const char *format, ...) {
    va_list ap;
    void *reply = NULL;
    va_start(ap, format);
    reply = redisvCommand(c, format, ap);
    va_end(ap);
    return reply;
}
1)采用不定参数的函数实现
2)调用redisvCommand函数来解析
  redisvFormatCommand(char **target, const char *format, va_list ap)
解析format字符串,根据format的内容从ap中取相应的数据;
  __redisAppendCommand(redisContext *c, char *cmd, size_t len) 将格式化的命令写到输出缓冲区中。

3 返回值

通过redisCommand函数的返回值来查看是否执行成功。当有错误发生时,返回值为NULL,同时err字段被标识。一旦context返回错误,则该context不能被重用,需要新建一个新的connect。

REDIS_REPLY_STATUS:

返回状态,状态字符串被放在reply->str中,状态字符串长度放在reply->len中。

REDIS_REPLY_ERROR:

返回错误

REDIS_REPLY_INTEGER:

返回整形值,该值被reply->integer接收,类型为long long

REDIS_REPLY_NIL:

返回一个nil对象,没有数据被接收。

REDIS_REPLY_STRING:

返回一个字符串,字符串被放在reply->str中,字符串长度放在reply->len中。

REDIS_REPLY_ARRAY:

多个元素被返回,元素的数量存放在reply->elements中。每个元素的返回值存放在redisReply中。

返回值需要被释放通过freeReplyObject()函数。

void freeReplyObject(void *reply) 根据reply->type的不同值执行不同的free操作。

4 清空

断开及清空context,使用下列函数:

void redisFree(redisContext *c);

该函数关闭socket,同时做释放操作。

根据不同对象,执行不同的free操作。

void redisFree(redisContext *c) {
    if (c->fd->0)
        close(c->fd);
    if (c->obuf != NULL) 
        sdsfree(c->obuf);
    if (c->reader != NULL) 
        redisReaderFree(c->reader);
    free(c);
}

5 发送命令

redisCommandArgv函数用来处理多个命令。

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

argc中存放命令数量;argv数组中存放string数组,argvlen为数组长度。

该函数返回值和redisCommand相同。

函数定义如下:

void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
    if (redisAppendCommandArgv(c, argc, argv, argvlen) != REDIS_OK) 
        return NULL;
    return __redisBlockForReply(c);
}

   int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen)
调用redisFormatCommandArgv函数解析输入命令集合
调用__redisAppendCommand函数格式化的命令放到输出缓冲区中
   __redisBlockForReply(redisContext *c)命令集合放到缓冲区中

e.g.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "hiredis.h"

int main(void) {
    unsigned int j;
    redisContext *c;
    redisReply *reply;

    struct timeval timeout = { 1, 500000 }; //1.5 seconds
    c = redisConnectWithTimeout((char*)"127.0.0.2", 6379, timeout);
    if (c->err) {
        printf("Connection error: %s\n", c->errstr);
        exit(1);
    }

    reply = redisCommand(c, "PING");
    printf("PING: %s\n", reply->str);
    freeReplyObject(reply);

    reply = redisCommand(c, "SET %s %s", "foo", "hello world");
    printf("SET: %s\n", reply->str);
    freeReplyObject(reply);

    reply = redisCommand(c, "SET %b %b "bar", 3, "hello", 5);
    printf("SET (binary API): %s\n", reply->str);
    freeReplyObject(reply);
    
    reply = redisCommand(c, "GET foo");
    printf("GET foo: %s\n", reply->str);
    freeReplyObject(reply);

    reply = redisCommand(c, "SET %s %s", "foo", "hello world");
    printf("SET: %s\n", reply->str);
    freeReplyObject(reply);

    reply = redisCommand(c, "SET %b %b", "bar", 3, "hello", 5);
    printf("SET (binary API): %s\n", reply->str);
    freeReplyObject(reply);

    reply = redisCommand(c, "GET foo");
    printf("GET foo: %s\n", reply->str);
    freeReplyObject(reply);

    reply = redisCommand(c, "INCR counter");
    printf("INCR counter: %lld\n", reply->integer);
    freeReplyObject(reply);

    reply = redisCommand(c, "INCR counter");
    printf("INCR counter: %lld\n", reply->integer);
    freeReplyObject(reply);

    reply = redisCommand(c, "DEL mylist");
    freeReplyObject(reply);
    for (j=0; j<10; j++) {
        char buf[64];

        snprintf(buf, 64, "%d", j);
        reply = redisCommand(c, "LPUSH mylist element-%s", buf);
        freeReplyObject(reply);
    }
    reply = redisCommand(c, "LRANGE mylist 0 -1");
    if (reply->type == REDIS_REPLY_ARRAY) {
        for (j=0; j<reply->elements; j++) {
            printf("%u) %s\n", j, reply->element[j]->str);
        }
    }
    freeReplyObject(reply);

    return 0;
}

result:
dcmvrh12% ./example
PING: PONG
SET: OK
SET (binary API): OK
GET foo: hello world
INCR counter: 9
INCR counter: 10
0) element-9
1) element-8
2) element-7
3) element-6
4) element-5
5) element-4
6) element-3
7) element-2
8) element-1
9) element-0
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值