Este artículo no establece una tabla de contenido por el momento, porque realmente no sé cómo configurarlo.
Como hiredis es una biblioteca dinámica, no sabía por dónde empezar desde el principio. Afortunadamente, los desarrolladores proporcionaron algunos casos de prueba, así que seguiré los pasos de los casos de prueba para analizarlos y aprender.
Si desea comenzar rápidamente, puede usar este tutorial: Aplique lo que ha aprendido: el funcionamiento de Redis en C ++
no necesita dedicar demasiado tiempo a este artículo. Recopile y léalo más tarde.
Si necesita un ejemplo completo, puede enviarme un mensaje privado.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis.h>
Este es el archivo de encabezado, #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;
Insertar:
Nota 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;
Bien, sigamos mirando el código de muestra.
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);
}
Insertar:
Nota 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()所产生的连接。
Ok, continuemos mirando el código de prueba:
/* PING server */
reply = redisCommand(c,"PING"); //注5:
printf("PING: %s\n", reply->str);
freeReplyObject(reply); //注6:
Insertar: redisCommand
Nota 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类型,以便做进一步的处理。
Nota 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所占用的内存。
Continuar hacia abajo:
/* 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;
}
El resultado de la operación final:
Si necesita un código de prueba completo, puede enviarme un mensaje privado