由于hiredis是个动态库,所以刚开始也不知道该从哪里下手,好在开发人员提供了一些测试案例,所以我就跟着测试案例的脚步来进行分析学习吧。
如果要快速上手,可以使用这一篇教程:学以致用 - C++操作redis
便无需在本篇耗费过多时间了,收藏一下,以后慢慢看
有需要完整示例,可以私信我。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis.h>
这是头文件,#include<hiredis/hiredis.h>
int main(int argc, char **argv) {
unsigned int j, isunix = 0;
redisContext *c; //注1:
redisReply *reply; //注2:
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
if (argc > 2) {
if (*argv[2] == 'u' || *argv[2] == 'U') {
isunix = 1;
/* in this case, host is the path to the unix socket */
printf("Will connect to unix socket @%s\n", hostname);
}
}
int port = (argc > 2) ? atoi(argv[2]) : 6379;
插入:
注1:redisContext
/* Context for a connection to Redis */
typedef struct redisContext {
const redisContextFuncs *funcs; /* Function table */
/*
typedef struct redisContextFuncs {
void (*free_privdata)(void *);
void (*async_read)(struct redisAsyncContext *);
void (*async_write)(struct redisAsyncContext *);
int (*read)(struct redisContext *, char *, size_t);
int (*write)(struct redisContext *);
} redisContextFuncs;
*/
int err; /* Error flags, 0 when there is no error */
char errstr[128]; /* String representation of error when applicable */
redisFD fd; /*
#ifndef _WIN32
typedef int redisFD;
#define REDIS_INVALID_FD -1
#else
#ifdef _WIN64
typedef unsigned long long redisFD; //SOCKET = 64-bit UINT_PTR
#else
typedef unsigned long redisFD; // SOCKET = 32-bit UINT_PTR
#endif
#define REDIS_INVALID_FD ((redisFD)(~0)) // INVALID_SOCKET
#endif
*/
int flags;
char *obuf; /* Write buffer */
redisReader *reader; /* Protocol reader */
/*
typedef struct redisReader {
int err; /* Error flags, 0 when there is no error
char errstr[128]; /* String representation of error when applicable
char *buf; /* Read buffer
size_t pos; /* Buffer cursor
size_t len; /* Buffer length
size_t maxbuf; /* Max length of unused buffer
redisReadTask rstack[9];
/*
typedef struct redisReadTask {
int type;
int elements; /* number of elements in multibulk container
int idx; /* index in parent (array) object
void *obj; /* holds user-generated value for a read task
struct redisReadTask *parent; /* parent task
void *privdata; /* user-settable arbitrary field
} redisReadTask;
int ridx; /* Index of current read task
void *reply; /* Temporary reply pointer
redisReplyObjectFunctions *fn;
void *privdata;
} redisReader;
*/
enum redisConnectionType connection_type;
/*
enum redisConnectionType {
REDIS_CONN_TCP,
REDIS_CONN_UNIX,
REDIS_CONN_USERFD
};
*/
struct timeval *timeout;
struct {
char *host;
char *source_addr;
int port;
} tcp;
struct {
char *path;
} unix_sock;
/* For non-blocking connect */
struct sockadr *saddr;
size_t addrlen;
/* Additional private data for hiredis addons such as SSL */
void *privdata;
} redisContext;
注2:redisReply
/* This is the reply object returned by redisCommand() */
typedef struct redisReply {
int type; /* REDIS_REPLY_* */
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
double dval; /* The double when type is REDIS_REPLY_DOUBLE */
size_t len; /* Length of string */
char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING
and REDIS_REPLY_DOUBLE (in additionl to dval). */
char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null
terminated 3 character content type, such as "txt". */
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReply;
好,我们继续看示例代码。
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
if (isunix) {
c = redisConnectUnixWithTimeout(hostname, timeout); //注3:
} else {
c = redisConnectWithTimeout(hostname, port, timeout);
}
if (c == NULL || c->err) {
if (c) {
printf("Connection error: %s\n", c->errstr);
redisFree(c); //注4:
} else {
printf("Connection error: can't allocate redis context\n");
}
exit(1);
}
插入:
注3:
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {
redisOptions options = {0};
/*
typedef struct {
/*
* the type of connection to use. This also indicates which
* `endpoint` member field to use
int type;
/* bit field of REDIS_OPT_xxx
int options;
/* timeout value. if NULL, no timeout is used
const struct timeval *timeout;
union {
/** use this field for tcp/ip connections
struct {
const char *source_addr;
const char *ip;
int port;
} tcp;
/** use this field for unix domain sockets
const char *unix_socket;
/**
* use this field to have hiredis operate an already-open
* file descriptor
redisFD fd;
} endpoint;
} redisOptions;
*/
REDIS_OPTIONS_SET_TCP(&options, ip, port);
/*
#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \
(opts)->type = REDIS_CONN_TCP; \
(opts)->endpoint.tcp.ip = ip_; \
(opts)->endpoint.tcp.port = port_;
*/
options.timeout = &tv;
return redisConnectWithOptions(&options); //注3.5
}
//该函数用来连接redis数据库, 两个参数分别是redis数据库的ip和端口,端口号一般为6379。
注3.5:redisConnectWithOptions
redisContext *redisConnectWithOptions(const redisOptions *options) {
redisContext *c = redisContextInit(options); //注3.5.5
if (c == NULL) {
return NULL;
}
if (!(options->options & REDIS_OPT_NONBLOCK)) {
c->flags |= REDIS_BLOCK;
}
if (options->options & REDIS_OPT_REUSEADDR) {
c->flags |= REDIS_REUSEADDR;
}
if (options->options & REDIS_OPT_NOAUTOFREE) {
c->flags |= REDIS_NO_AUTO_FREE;
}
if (options->type == REDIS_CONN_TCP) {
redisContextConnectBindTcp(c, options->endpoint.tcp.ip,
options->endpoint.tcp.port, options->timeout,
options->endpoint.tcp.source_addr);
} else if (options->type == REDIS_CONN_UNIX) {
redisContextConnectUnix(c, options->endpoint.unix_socket,
options->timeout);
} else if (options->type == REDIS_CONN_USERFD) {
c->fd = options->endpoint.fd;
c->flags |= REDIS_CONNECTED;
} else {
// Unknown type - FIXME - FREE
return NULL;
}
if (options->timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
redisContextSetTimeout(c, *options->timeout);
}
return c;
}
注3.5.5:redisContextInit
static redisContext *redisContextInit(const redisOptions *options) {
redisContext *c;
c = calloc(1, sizeof(*c));
if (c == NULL)
return NULL;
c->funcs = &redisContextDefaultFuncs;
c->obuf = sdsempty();
c->reader = redisReaderCreate();
c->fd = REDIS_INVALID_FD;
if (c->obuf == NULL || c->reader == NULL) {
redisFree(c);
return NULL;
}
(void)options; /* options are used in other functions */
return c;
}
注4:redisFree
void redisFree(redisContext *c) {
if (c == NULL)
return;
redisNetClose(c);
sdsfree(c->obuf);
redisReaderFree(c->reader);
free(c->tcp.host);
free(c->tcp.source_addr);
free(c->unix_sock.path);
free(c->timeout);
free(c->saddr);
if (c->funcs->free_privdata) {
c->funcs->free_privdata(c->privdata);
}
memset(c, 0xff, sizeof(*c));
free(c);
}
//释放redisConnect()所产生的连接。
好,我们继续往下看测试代码:
/* PING server */
reply = redisCommand(c,"PING"); //注5:
printf("PING: %s\n", reply->str);
freeReplyObject(reply); //注6:
插入:redisCommand
注5:
void *redisCommand(redisContext *c, const char *format, ...) {
va_list ap;
va_start(ap,format);
void *reply = redisvCommand(c,format,ap);
va_end(ap);
return reply;
}
//该函数用于执行redis数据库中的命令,第一个参数为连接数据库返回的redisContext,剩下的参数为变参.。
//此函数的返回值为void*,但是一般会强制转换为redisReply类型,以便做进一步的处理。
注6:freeReplyObject
/* Free a reply object */
void freeReplyObject(void *reply) {
redisReply *r = reply;
size_t j;
if (r == NULL)
return;
switch(r->type) {
case REDIS_REPLY_INTEGER:
break; /* Nothing to free */
case REDIS_REPLY_ARRAY:
case REDIS_REPLY_MAP:
case REDIS_REPLY_SET:
if (r->element != NULL) {
for (j = 0; j < r->elements; j++)
freeReplyObject(r->element[j]);
free(r->element);
}
break;
case REDIS_REPLY_ERROR:
case REDIS_REPLY_STATUS:
case REDIS_REPLY_STRING:
case REDIS_REPLY_DOUBLE:
free(r->str);
break;
}
free(r);
}
//释放redisCommand执行后返回的的redisReply所占用的内存。
继续往下:
/* Set a key */
reply = redisCommand(c,"SET %s %s", "foo", "hello world");
printf("SET: %s\n", reply->str);
freeReplyObject(reply);
/* Set a key using binary safe API */
reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5);
printf("SET (binary API): %s\n", reply->str);
freeReplyObject(reply);
/* Try a GET and two INCR */
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);
/* again ... */
reply = redisCommand(c,"INCR counter");
printf("INCR counter: %lld\n", reply->integer);
freeReplyObject(reply);
/* Create a list of numbers, from 0 to 9 */
reply = redisCommand(c,"DEL mylist");
freeReplyObject(reply);
for (j = 0; j < 10; j++) {
char buf[64];
snprintf(buf,64,"%u",j);
reply = redisCommand(c,"LPUSH mylist element-%s", buf);
freeReplyObject(reply);
}
/* Let's check what we have inside the list */
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);
/* Disconnects and frees the context */
redisFree(c);
return 0;
}
最后运行出来的结果呢: