前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入浅出hiredis -- C++操作redis

深入浅出hiredis -- C++操作redis

作者头像
看、未来
发布2020-09-18 10:23:47
2.8K0
发布2020-09-18 10:23:47
举报

由于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;
}

最后运行出来的结果呢:

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-09-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档