redis使用c++ API 的hiredis连接详解

    Redis是一个NoSQL数据库,本文将实现一个用c++ API接口连接的例子,来实现对Redis数据库的写入和读出功能,具体将从Redis的安装,Redis的c++接口hiredis安装,代码演示三部分组成。

一、Redis的安装和配置

    由于源中已有Redis的相关组件,这里就不进行源码编译而直接使用apt-get (ubuntu环境)下载和安装

1.redis的安装和配置
sudo apt-cache search redis        //根据结果列表找到需要安装的软件包:redis-server
sudo apt-get install redis-server  //安装软件

2.配置文件
whereis resids                    //查看redis位置: /etc/redis
cd  /etc/redis                    //进入文件夹
/etc/redis$ ls -l                 //显示文件,其中redis.conf为配置文件
总用量 60
-rw-r----- 1 redis redis 41623 12月 19  2015 redis.conf
drwxr-xr-x 2 root  root   4096 9月  21 10:47 redis-server.post-down.d
drwxr-xr-x 2 root  root   4096 9月  21 10:47 redis-server.post-up.d
drwxr-xr-x 2 root  root   4096 9月  21 10:47 redis-server.pre-down.d
drwxr-xr-x 2 root  root   4096 9月  21 10:47 redis-server.pre-up.d

3.启动
服务端:redis-server (使用默认端口) (--port 6599 加端口)
客户端:redis-cli    (连接之前测试启动指令 redis-cli ping 返回PONG启动成功)

4.关闭:redis-cli    (-p 6380 可指定端口)shutdown

二、redis的c++api接口 hiredis安装

    实际上hiredis是一个c的接口,同样使用apt-get安装hiredis,GitHub上有他的完整工程项目,点此转到

sudo apt-cache search hiredis // 查看发现c语言开发库为libhiredis-dev
libhiredis-dbg - minimalistic C client library for Redis (debug)
libhiredis-dev - minimalistic C client library for Redis (development files)
libhiredis0.13 - minimalistic C client library for Redis
python-hiredis - redis protocol reader for Python 2.X using hiredis

sudo apt-get install libhiredis-dev  //选择并安装

     hiredis库目录的位置为默认的 /usr/lib/x86_64-linux-gnu/下,头文件在 /usr/include/hiredis 下,hiredis头文件中定义了Redis的连接的方式redisConnect()等方法,连接信息存储在上下文redisContext的结构体对象中,通过redisCommand()等方法进行具体的数据库存取指令操作并返回相关信息在redisReply的结构体对象中,不要忘了freeReplyObject(void *reply)释放redisReply连接响应对象,redisFree()函数释放redisContext上下文对象,具体的定义和方法请看以下代码。

#ifndef __HIREDIS_H
#define __HIREDIS_H
#include "read.h"
#include <stdarg.h> /* for va_list */
#include <sys/time.h> /* for struct timeval */
#include <stdint.h> /* uintXX_t, etc */
#include "sds.h" /* for sds */

#define HIREDIS_MAJOR 0
#define HIREDIS_MINOR 13
#define HIREDIS_PATCH 3
#define HIREDIS_SONAME 0.13

#ifdef __cplusplus
extern "C" {
#endif

/* This is the reply object returned by redisCommand()
执行redis数据库指令操作的响应信息封装在redisReply的结构体中
 */
typedef struct redisReply {
    int type; /* REDIS_REPLY_* */
    long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
    int len; /* Length of string 存储字符串长度 */
    char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING 错误信息和返回的string类型*/
    size_t elements; /* number of elements, for REDIS_REPLY_ARRAY 如果为数组存储数组长度*/
    struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY 存储数组元素向量*/
} redisReply;

redisReader *redisReaderCreate(void);

/* Function to free the reply objects hiredis returns by default.
   释放响应对象
 */
void freeReplyObject(void *reply);

/* Functions to format a command according to the protocol.
   数据库操作相关语句
 */
int redisvFormatCommand(char **target, const char *format, va_list ap);
int redisFormatCommand(char **target, const char *format, ...);
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
void redisFreeCommand(char *cmd);
void redisFreeSdsCommand(sds cmd);

enum redisConnectionType {
    REDIS_CONN_TCP,
    REDIS_CONN_UNIX,
};

/* Context for a connection to Redis
    建立上下文连接对象的结构体
 */
typedef struct redisContext {
    int err; /* Error flags, 0 when there is no error错误标志,0表示没有错误 */
    char errstr[128]; /* String representation of error when applicable 错误声明 */
    int fd;
    int flags;
    char *obuf; /* Write buffer */
    redisReader *reader; /* Protocol reader */

    enum redisConnectionType connection_type;
    struct timeval *timeout;  //设置连接等待时间

    struct {
        char *host;
        char *source_addr;
        int port;
    } tcp;

    struct {
        char *path;
    } unix_sock;

} redisContext;

//建立上下文连接
redisContext *redisConnect(const char *ip, int port);
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
redisContext *redisConnectNonBlock(const char *ip, int port);
redisContext *redisConnectBindNonBlock(const char *ip, int port,
                                       const char *source_addr);
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
                                                const char *source_addr);
redisContext *redisConnectUnix(const char *path);
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
redisContext *redisConnectUnixNonBlock(const char *path);
redisContext *redisConnectFd(int fd);

/**
 * Reconnect the given context using the saved information.
 *
 * This re-uses the exact same connect options as in the initial connection.
 * host, ip (or path), timeout and bind address are reused,
 * flags are used unmodified from the existing context.
 *
 * Returns REDIS_OK on successfull connect or REDIS_ERR otherwise.
 */
int redisReconnect(redisContext *c);

int redisSetTimeout(redisContext *c, const struct timeval tv);
int redisEnableKeepAlive(redisContext *c);
void redisFree(redisContext *c);
int redisFreeKeepFd(redisContext *c);
int redisBufferRead(redisContext *c);
int redisBufferWrite(redisContext *c, int *done);

/* In a blocking context, this function first checks if there are unconsumed
 * replies to return and returns one if so. Otherwise, it flushes the output
 * buffer to the socket and reads until it has a reply. In a non-blocking
 * context, it will return unconsumed replies until there are no more. */
int redisGetReply(redisContext *c, void **reply);
int redisGetReplyFromReader(redisContext *c, void **reply);

/* Write a formatted command to the output buffer. Use these functions in blocking mode
 * to get a pipeline of commands. */
int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);

/* Write a command to the output buffer. Use these functions in blocking mode
 * to get a pipeline of commands. */
int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
int redisAppendCommand(redisContext *c, const char *format, ...);
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

/* Issue a command to Redis. In a blocking context, it is identical to calling
 * redisAppendCommand, followed by redisGetReply. The function will return
 * NULL if there was an error in performing the request, otherwise it will
 * return the reply. In a non-blocking context, it is identical to calling
 * only redisAppendCommand and will always return NULL. */
void *redisvCommand(redisContext *c, const char *format, va_list ap);
void *redisCommand(redisContext *c, const char *format, ...);
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

#ifdef __cplusplus
}
#endif

#endif

    在read.h文件中有redisReply结构体宏的定义,int type的类型:

#ifndef __HIREDIS_READ_H
#define __HIREDIS_READ_H

#define REDIS_ERR -1
#define REDIS_OK 0

#define REDIS_ERR_IO 1 /* Error in read or write */
#define REDIS_ERR_EOF 3 /* End of file */
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
#define REDIS_ERR_OOM 5 /* Out of memory */
#define REDIS_ERR_OTHER 2 /* Everything else... */

#define REDIS_REPLY_STRING 1    //存放在char *str
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3   //integer存储为数据条数
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5   //成功状态码为:"OK"  存放在char *str
#define REDIS_REPLY_ERROR 6    //存放在char *str

     如hiredis.c文件中看freeReplyObject函数对以上响应状态的处理:

/* Free a reply object 
redisReply的类型有:REDIS_REPLY_INTEGER:
REDIS_REPLY_ERROR:
REDIS_REPLY_STATUS:
REDIS_REPLY_STRING:这三个都是返回字符串
*/
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:
        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:
        free(r->str);
        break;
    }
    free(r);
}

三、Redis使用hiredis API连接

代码结构如图所示:

具体代码如下:配置文件

#ifndef REDISCONFIG_H
#define REDISCONFIG_H
#include <string>

class RedisConfig
{
public:
    RedisConfig();
    std::string getRedisIP();//获取ip
    int getRedisPort();//获取端口号

};

#endif
#include "RedisConfig.h"
#include <sstream>

RedisConfig::RedisConfig()
{

}

std::string RedisConfig::getRedisIP()
{
    return "127.0.0.1";//设置为本机ip
}

int RedisConfig::getRedisPort()
{
    return 6379;
}

    具体处理代码:包含了string类型和list类型的存取代码,根据redisReply响应的信息来进行信息的处理,对于其他类型对响应状态的判断可以通过命令行操作来确定,如图1所示。

#ifndef _H_REDIS_TOOLS_
#define _H_REDIS_TOOLS_
#include <iostream>
#include <vector>
#include <hiredis/hiredis.h>
#include <RedisConfig.h>

using namespace std;

class RedisTool
{
public:
    RedisTool();
    ~RedisTool();
    int setString(string key, string value);
    string getString(string key);

    int setList(string key,vector<int> value);
    vector<int> getList(string key);

private:
    void init();
    redisContext *m_redis;
    RedisConfig m_config;
};

#endif
#include <RedisTools.h>
#include <RedisConfig.h>
#include <stdio.h>
#include <string.h>
#include <sstream>

RedisTool::RedisTool()
{
    m_redis = NULL;
	init();
}
RedisTool::~RedisTool()
{
    if(m_redis != NULL)
	{
        redisFree(m_redis);//析构函数释放资源
		cout << "~RedisTool :: free redis connection " << endl;
	}
}

void RedisTool::init()
{
    struct timeval timeout = { 1, 500000 }; // 1.5 seconds 设置连接等待时间
	char ip[255];	
    strcpy(ip, m_config.getRedisIP().c_str());
	cout << "init : ip = " << ip << endl;
    m_redis = redisConnectWithTimeout(ip, m_config.getRedisPort(), timeout);//建立连接
    if (m_redis->err) {
        printf("RedisTool : Connection error: %s\n", m_redis->errstr);
    }	
	else
	{
		cout << "init redis tool success " << endl;
        //REDIS_REPLY响应的类型type
        cout << "#define REDIS_REPLY_STRING 1"<< endl;
        cout << "#define REDIS_REPLY_ARRAY 2"<< endl;
        cout << "#define REDIS_REPLY_INTEGER 3"<< endl;
        cout << "#define REDIS_REPLY_NIL 4"<< endl;
        cout << "#define REDIS_REPLY_STATUS 5"<< endl;
        cout << "#define REDIS_REPLY_ERROR 6"<< endl;
	}
}

//向数据库写入string类型数据
int RedisTool::setString(string key, string value)
{
    if(m_redis == NULL || m_redis->err)//int err; /* Error flags, 错误标识,0表示无错误 */
    {
        cout << "Redis init Error !!!" << endl;
        init();
        return -1;
    }
    redisReply *reply;
    reply = (redisReply *)redisCommand(m_redis,"SET %s %s", key.c_str(), value.c_str());//执行写入命令
    cout<<"set string type = "<<reply->type<<endl;//获取响应的枚举类型
    int result = 0;
    if(reply == NULL)
    {
        redisFree(m_redis);
        m_redis = NULL;
        result = -1;
        cout << "set string fail : reply->str = NULL " << endl;
        //pthread_spin_unlock(&m_redis_flock);
        return -1;
    }
    else if(strcmp(reply->str, "OK") == 0)//根据不同的响应类型进行判断获取成功与否
    {
        result = 1;
    }
    else
    {
        result = -1;
        cout << "set string fail :" << reply->str << endl;
    }
    freeReplyObject(reply);//释放响应信息

    return result;
}

//从数据库读出string类型数据
string RedisTool::getString(string key)
{
    if(m_redis == NULL || m_redis->err)
	{
		cout << "Redis init Error !!!" << endl;
		init();
        return NULL;
	}
	redisReply *reply;
    reply = (redisReply *)redisCommand(m_redis,"GET %s", key.c_str());
    cout<<"get string type = "<<reply->type<<endl;

	if(reply == NULL)
	{
        redisFree(m_redis);
        m_redis = NULL;
        cout << "ERROR getString: reply = NULL!!!!!!!!!!!! maybe redis server is down" << endl;
        return NULL;
	}
    else if(reply->len <= 0)
	{		
        freeReplyObject(reply);
        return NULL;
	}
	else
	{
        stringstream ss;
        ss << reply->str;
        freeReplyObject(reply);
        return ss.str();
    }
}

//向数据库写入vector(list)类型数据
int RedisTool::setList(string key,vector<int> value)
{
    if(m_redis == NULL || m_redis->err)
    {
        cout << "Redis init Error !!!" << endl;
        init();
        return -1;
    }

    redisReply *reply;

    int valueSize = value.size();
    int result = 0;

    for(int i=0; i<valueSize; i++)
    {
        reply = (redisReply*)redisCommand(m_redis,"RPUSH %s %d", key.c_str(),value.at(i));
        cout<<"set list type = "<<reply->type<<" ";
        int old = reply->integer;
        if(reply == NULL)
        {
            redisFree(m_redis);
            m_redis = NULL;
            result = -1;
            cout << "set list fail : reply->str = NULL " << endl;
            //pthread_spin_unlock(&m_redis_flock);
            return -1;
        }
        else if(reply->integer == old++)
        {
            result = 1;
            cout<<"rpush list ok"<<endl;
            continue;
        }
        else
        {
            result = -1;
            cout << "set list fail ,reply->integer = " << reply->integer << endl;
            return -1;
        }

    }

    freeReplyObject(reply);
    cout<<"set List  success"<<endl;
    return result;

}

//从数据库读出vector(list)类型数据
vector<int> RedisTool::getList(string key)
{

    if(m_redis == NULL || m_redis->err)
    {
        cout << "Redis init Error !!!" << endl;
        init();
        return vector<int>{};//返回空的向量
    }

    redisReply *reply;
    reply = (redisReply*)redisCommand(m_redis,"LLEN %s", key.c_str());
    int valueSize = reply->integer;
    cout<<"List size is :"<<reply->integer<<endl;

    reply = (redisReply*)redisCommand(m_redis,"LRANGE %s %d %d", key.c_str(),0,valueSize-1);
    cout<<"get list type = "<<reply->type<<endl;
    cout<<"get list size = "<<reply->elements<<endl;//对于数组类型可以用elements元素获取数组长度

    redisReply** replyVector = reply->element;//获取数组指针
    vector<int> result;
    for(int i=0;i<valueSize;i++)
    {
        string temp =(*replyVector)->str;//遍历redisReply*数组,存入vector向量
        int a =atoi(temp.c_str());
        result.push_back(a);
        replyVector++;
    }

    cout<<"result size:"<<result.size()<<endl;
    return result;

}

    main函数:

#include <iostream>
#include <vector>
#include <RedisTools.h>

using namespace std;


int main()
{
    RedisTool redis;
    //测试 string
    redis.setString("wangbaojia","test1");
    string result = redis.getString("wangbaojia");
    cout<<"result="<<result<<endl;
    //测试list
    vector<int> vec ={1,2,3,4};
    redis.setList("bao",vec);
    vector<int> vecResult = redis.getList("bao");
    for(int i=0;i<vecResult.size();i++)
    {
        cout<<vecResult[i]<<" ";
    }

    return 0;
}

      运行结果:

 四、完整代码下载   

点击下载                                                                  

  • 18
    点赞
  • 124
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
要在C++语言中调用Redis,可以使用RedisC++客户端库。以下是使用Redis C++客户端库的一些基本步骤: 1. 首先,确保你已经安装了Redis,并且可以正常运行。 2. 下载并安装Redis C++客户端库。推荐使用hiredis库,它是一个基于C的库,提供了与Redis交互的API。你可以从官方GitHub仓库下载并编译安装该库。 3. 在你的C++项目中包含Redis C++客户端库的头文件。 4. 连接Redis服务器。使用hiredis库,你可以使用以下代码连接Redis服务器: ```cpp #include <hiredis/hiredis.h> int main() { redisContext* redis = redisConnect("localhost", 6379); if (redis == NULL || redis->err) { if (redis) { printf("Error: %s\n", redis->errstr); redisFree(redis); } else { printf("Can't allocate redis context\n"); } return 1; } // 连接成功,可以进行后续操作 // ... // 关闭连接 redisFree(redis); return 0; } ``` 5. 在连接成功后,你可以使用hiredis库提供的API执行各种Redis命令。例如,以下代码演示了如何执行SET和GET命令: ```cpp redisReply* reply = (redisReply*)redisCommand(redis, "SET key value"); freeReplyObject(reply); reply = (redisReply*)redisCommand(redis, "GET key"); if (reply->type == REDIS_REPLY_STRING) { printf("GET key: %s\n", reply->str); } freeReplyObject(reply); ``` 这里的`redisCommand`函数用于执行Redis命令,并返回一个`redisReply`对象,你可以通过检查其`type`字段来确定返回值的类型,然后获取相应的值。需要注意的是,使用完`redisReply`对象后,需要使用`freeReplyObject`函数释放它。 除了SET和GET命令,Redis还支持其他各种命令(例如HSET、HGET、LPUSH、LPOP等),你可以根据自己的需求使用适当的命令。 总之,以上是在C++语言中调用Redis的基本步骤。你可以根据具体情况和需求,使用Redis C++客户端库执行各种操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值