一、编译配置hiredis.h
C++来操作redis数据库。通过hiredis接口来实现,目前只能在Linux环境使用。
-
下载hiredis.h
-
hiredis的下载地址为:
https://github.com/redis/hiredis -
解压并编译hiredis
[root@localhost source_code]# pwd
/usr/local/source_code
[root@localhost source_code]# ll
总用量 5296
drwxr-xr-x. 2 root root 42 9月 18 17:32 c_demo
-rw-r--r--. 1 root root 126216 10月 10 09:42 hiredis-1.2.0.tar.gz
drwxr-xr-x. 5 500 users 8192 9月 4 15:28 jpeg-9e
-rw-r--r--. 1 root root 1046935 9月 4 15:23 jpegsrc.v9e.tar.gz
-rw-r--r--. 1 root root 4234219 8月 31 16:22 release-3.4.13.tar.gz
drwxrwxr-x. 8 root root 259 8月 31 16:33 zookeeper-release-3.4.13
[root@localhost source_code]# tar -zxvf hiredis-1.2.0.tar.gz
hiredis-1.2.0/
hiredis-1.2.0/.github/
hiredis-1.2.0/.github/release-drafter-config.yml
-
执行
make && make install
(自动把libhiredis.so放到/usr/local/lib/中,把hiredis.h放到/usr/local/inlcude/hiredis/中
)[root@localhost source_code]# cd hiredis-1.2.0
[root@localhost hiredis-1.2.0]# make && make install
cc -std=c99 -c -O3 -fPIC -Wall -Wextra -Werror -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers -g -ggdb -pedantic alloc.c
cc -std=c99 -c -O3 -fPIC -Wall -Wextra -Werror -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers -g -ggdb -pedantic net.c
cc -std=c99 -c -O3 -fPIC -Wall -Wextra -Werror -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers -g -ggdb -pedantic hiredis.c
生成hredis 相关信息
mkdir -p /usr/local/include/hiredis /usr/local/include/hiredis/adapters /usr/local/lib
-- hiredis 头文件生成地址
cp -pPR hiredis.h async.h read.h sds.h alloc.h sockcompat.h /usr/local/include/hiredis
cp -pPR adapters/*.h /usr/local/include/hiredis/adapters
-- hiredis 动态库文件生成地址
cp -pPR libhiredis.so /usr/local/lib/libhiredis.so.1.1.0
cd /usr/local/lib && ln -sf libhiredis.so.1.1.0 libhiredis.so && ln -sf libhiredis.so.1.1.0 libhiredis.so.1
cp -pPR libhiredis.a /usr/local/lib
mkdir -p /usr/local/lib/pkgconfig
cp -pPR hiredis.pc /usr/local/lib/pkgconfig
在程序中包含#include <hiredis/hiredis.h>
即可
[root@localhost ~]# cd /usr/local/source_code/
[root@localhost source_code]# ll
总用量 5300
drwxr-xr-x. 2 root root 42 9月 18 17:32 c_demo
drwxrwxr-x. 6 root root 4096 10月 10 09:44 hiredis-1.2.0
-rw-r--r--. 1 root root 126216 10月 10 09:42 hiredis-1.2.0.tar.gz
drwxr-xr-x. 2 root root 50 10月 10 10:37 hiredis_demo
drwxr-xr-x. 5 500 users 8192 9月 4 15:28 jpeg-9e
-rw-r--r--. 1 root root 1046935 9月 4 15:23 jpegsrc.v9e.tar.gz
-rw-r--r--. 1 root root 4234219 8月 31 16:22 release-3.4.13.tar.gz
drwxr-xr-x. 2 root root 6 10月 10 10:58 zookeeper_demo
drwxrwxr-x. 8 root root 259 8月 31 16:33 zookeeper-release-3.4.13
[root@localhost source_code]# cd hiredis_demo/
[root@localhost hiredis_demo]# ll
总用量 16
-rwxr-xr-x. 1 root root 8608 10月 10 10:37 hiredis_demo
-rw-r--r--. 1 root root 697 10月 10 10:26 hiredis_demo.cpp
[root@localhost hiredis_demo]# cat hiredis_demo.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis/hiredis.h>
int main() {
redisContext *c;
redisReply *reply;
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
c = redisConnectWithTimeout((char*)"192.168.43.80", 6379, timeout);
if (c != NULL && c->err)
{
printf("Connection error: %s", c->errstr);
redisFree(c);
exit(1);
}
reply = (redisReply *)redisCommand(c, "SET %s %s", "foo", "bar");
freeReplyObject(reply);
reply = (redisReply *)redisCommand(c, "GET foo");
printf("foo: %s", reply->str);
freeReplyObject(reply);
redisFree(c);
return 0;
}
[root@localhost hiredis_demo]#
-
在
编译代码的时候需要加链接的库及库的路径
,那么编译命令如下g++ hiredis_demo.cpp -o hiredis_demo -L/usr/local/lib/ -lhiredis
-
在执行的时候
如果出现动态库无法加载。
[root@localhost hiredis_demo]# ./hiredis_demo ./hiredis_demo: error while loading shared libraries: libhiredis.so.1.1.0: cannot open shared object file: No such file or directory
那么需要进行如下配置。
-
在 /etc/ld.so.conf.d/ 目录下新建文件 usr-libs.conf ,内容是: /usr/local/lib
vim /etc/ld.so.conf.d/usr-libs.conf
-
然后使用命令 /sbin/ldconfig 更新一下配置即可。
sbin/ldconfig
二、hiredis.h核心方法
第一步:查看hiredis.h 头文件定义:
[root@localhost lib]# cd /usr/local/include/hiredis/
[root@localhost hiredis]# ll
总用量 56
drwxr-xr-x. 2 root root 197 10月 10 09:44 adapters
-rw-rw-r--. 1 root root 3130 7月 12 15:31 alloc.h
-rw-rw-r--. 1 root root 6288 7月 12 15:31 async.h
-rw-rw-r--. 1 root root 14319 7月 12 15:31 hiredis.h
-rw-rw-r--. 1 root root 4918 7月 12 15:31 read.h
-rw-rw-r--. 1 root root 9238 7月 12 15:31 sds.h
-rw-rw-r--. 1 root root 4409 7月 12 15:31 sockcompat.h
[root@localhost hiredis]# cat hiredis.h
核心方法定义
1.建立链接:redisConnect
redisContext* redisConnect(const char *ip, int port)
函数说明:
- 函数用来连接redis数据库, 两个参数分别是redis数据库的ip和端口,端口号一般为6379。
- 该函数redisConnect用于创建所谓的redisContext。上下文是Hiredis保持连接状态的地方。
当连接处于错误状态时,该redisContext 结构具有一个err非零的整数字段
。该字段errstr将包含带有错误描述的字符串。
- 使用尝试连接到Redis后redisConnect,
应检查该err字段以查看建立连接是否成功
还提供了一个函数,供连接超时限定
,即
redisContext* redisConnectWithTimeout(const char *ip,
int port, timeval tv)。
Demo 示例:
c = redisConnectWithTimeout((char*)"192.168.43.80", 6379, timeout);
if (c != NULL && c->err)
{
printf("Connection error: %s", c->errstr);
redisFree(c);
exit(1);
}
解释说明:
当函数调用不成功时,取决于函数NULL还是REDIS_ERR返回。err上下文中的字段将为非零值,并设置为以下常量之一:
- REDIS_ERR_IO:创建连接,尝试写入套接字或从套接字读取时发生I /O错误。如果您包含errno.h在应用程序中,则可以使用全局errno变量来找出问题所在。
- REDIS_ERR_EOF:服务器关闭了连接,导致读取为空。
- REDIS_ERR_PROTOCOL:解析协议时出错。
- REDIS_ERR_OTHER:其他任何错误。当前,仅在无法解析要连接的指定主机名时使用。
在每种情况下,errstr上下文中的字段都将设置为包含错误的字符串表示形式。
2. 执行redis命令:redisCommand
void *redisCommand(redisContext *c, const char *format...)
函数说明:
-
该函数用于执行redis数据库中的命令,
第一个参数为连接数据库返回的redisContext,剩下的参数为变参。
-
此函数的返回值为void*,但是一般会强制转换为redisReply类型,以便做进一步的处理。
const char *format... 动态参数关联方法定义:
void * redisCommandArgv(redisContext * c,int argc,
const char ** argv,const size_t * argvlen);
函数说明:
- 它需要参数的数量,argc字符串数组argv和参数的长度argvlen。
- 为了方便起见,argvlen可以将设置为NULL,并且函数将strlen(3)在每个参数上使用以确定其长度。
- 显然,当任何一个参数需要二进制安全时,argvlen都应提供整个长度数组。
- 返回值的语义与相同redisCommand。
Demo 示例:
reply = (redisReply *)redisCommand(c, "SET %s %s", "foo", "bar");
3.释放redisCommand
void freeReplyObject(void *reply)
函数说明:
- 释放redisCommand执行后返回的的redisReply所占用的内存。
- redisCommand
成功执行命令后,的返回值将保留
答复。 发生错误时,返回值为NULL并且err将设置上下文中的字段
。返回错误后,上下文context将无法重用,您应该建立一个新的连接。
Demo 示例:
freeReplyObject(reply);
4.断开连接:redisFree
void redisFree(redisContext *c)
函数说明:
- 此函数立即
关闭套接字
,然后释放在创建上下文时完成的分配。
三、Hiredis 基于C++ 封装
redis_handler.h
#ifndef __REDIS_HANDLER_H__
#define __REDIS_HANDLER_H__
#include <hiredis/hiredis.h>
#include <string>
using namespace std;
enum
{
M_REDIS_OK = 0, //执行成功
M_CONNECT_FAIL = -1, //连接redis失败
M_CONTEXT_ERROR = -2, //RedisContext返回错误
M_REPLY_ERROR = -3, //redisReply错误
M_EXE_COMMAND_ERROR = -4 //redis命令执行错误
};
class RedisHandler
{
public:
RedisHandler();
~RedisHandler();
int connect(const string &addr, int port, const string &pwd = ""); //连接redis数据库:addr:IP地址,port:端口号,pwd:密码(默认为空)
int disConnect(); //断开连接
int setValue(const string &key, const string &value); //添加或修改键值对,成功返回0,失败<0
int getValue(const string &key, string &value); //获取键对应的值,成功返回0,失败<0
int delKey(const string &key); //删除键,成功返回影响的行数,失败<0
int printAll(); //打印所有的键值对
string getErrorMsg(); //获取错误信息
private:
string m_addr; //IP地址
int m_port; //端口号
string m_pwd; //密码
redisContext* pm_rct; //redis结构体
redisReply* pm_rr; //返回结构体
string error_msg; //错误信息
int connectAuth(const string &pwd); //使用密码登录
int handleReply(void* value = NULL, redisReply ***array = NULL); //处理返回的结果
};
#endif
redis_handler.cpp
#include "redis_handler.h"
#include <string>
#include <cstring>
#include <iostream>
using namespace std;
RedisHandler::RedisHandler()
{
m_addr = "";
m_port = 0;
m_pwd = "";
pm_rct = NULL;
pm_rr = NULL;
error_msg = "";
}
RedisHandler::~RedisHandler()
{
disConnect();
pm_rct = NULL;
pm_rr = NULL;
}
/*
连接redis数据库
addr: 地址,port:端口号,pwd:密码
成功返回M_REDIS_OK,失败返回M_CONNECT_FAIL
*/
int RedisHandler::connect(const string &addr = "127.0.0.1", int port = 6379, const string &pwd) {
m_addr = addr;
m_port = port;
m_pwd = pwd;
pm_rct = redisConnect(m_addr.c_str(), m_port);
if (pm_rct->err)
{
error_msg = pm_rct->errstr;
return M_CONNECT_FAIL;
}
if (!m_pwd.empty())
{
return connectAuth(m_pwd);
}
return M_REDIS_OK;
}
/*
断开redis连接
*/
int RedisHandler::disConnect()
{
redisFree(pm_rct);
freeReplyObject(pm_rr);
}
/*
添加或插入键值对
key:键,value:值
成功返回M_REDIS_OK,失败返回<0
*/
int RedisHandler::setValue(const string &key, const string &value)
{
string cmd = "set " + key + " " + value;
pm_rr = (redisReply*)redisCommand(pm_rct, cmd.c_str());
return handleReply();
}
/*
获取键对应的值
key:键,value:值引用
成功返回M_REDIS_OK,失败返回<0
*/
int RedisHandler::getValue(const string &key, string &value)
{
string cmd = "get " + key;
pm_rr = (redisReply*)redisCommand(pm_rct, cmd.c_str());
int ret = handleReply(&value);
}
/*
删除键
key:键
成功返回影响的行数(可能为0),失败返回<0
*/
int RedisHandler::delKey(const string &key)
{
string cmd = "del " + key;
pm_rr = (redisReply*)redisCommand(pm_rct, cmd.c_str());
int rows = 0;
int ret = handleReply(&rows);
if (ret == M_REDIS_OK)
return rows;
else
return ret;
}
/*
打印所有键值对到屏幕上
*/
int RedisHandler::printAll()
{
string cmd = "keys *";
pm_rr = (redisReply*)redisCommand(pm_rct, cmd.c_str());
int len ;
redisReply **array;
int ret = handleReply(&len, &array);
if (ret == M_REDIS_OK)
{
for (int i = 0; i < len; i++)
cout << string(array[i]->str) << endl;
}
else
return 0;
}
/*
返回错误信息
*/
string RedisHandler::getErrorMsg()
{
return error_msg;
}
/*
使用密码登录
psw:登录密码
成功返回M_REDIS_OK,失败返回<0
*/
int RedisHandler::connectAuth(const string &psw)
{
string cmd = "auth " + psw;
pm_rr = (redisReply*)redisCommand(pm_rct, cmd.c_str());
return handleReply();
}
/*
处理redis返回的信息
value:数据指针,用于保存redis返回的基本类型(value指针指向该数据)
array:数组指针,用于保存redis返回的数组
成功返回M_REDIS_OK,失败返回<0
*/
int RedisHandler::handleReply(void* value, redisReply*** array)
{
if (pm_rct->err)
{
error_msg = pm_rct->errstr;
return M_CONTEXT_ERROR;
}
if (pm_rr == NULL)
{
error_msg = "auth redisReply is NULL";
return M_REPLY_ERROR;
}
switch (pm_rr->type)
{
case REDIS_REPLY_ERROR:
error_msg = pm_rr->str;
return M_EXE_COMMAND_ERROR;
case REDIS_REPLY_STATUS:
if (!strcmp(pm_rr->str, "OK"))
return M_REDIS_OK;
else
{
error_msg = pm_rr->str;
return M_EXE_COMMAND_ERROR;
}
case REDIS_REPLY_INTEGER:
*(int*)value = pm_rr->integer;
return M_REDIS_OK;
case REDIS_REPLY_STRING:
*(string*)value = pm_rr->str;
return M_REDIS_OK;
case REDIS_REPLY_NIL:
*(string*)value = "";
return M_REDIS_OK;
case REDIS_REPLY_ARRAY:
*(int*)value = pm_rr->elements;
*array = pm_rr->element;
return M_REDIS_OK;
default:
error_msg = "unknow reply type";
return M_EXE_COMMAND_ERROR;
}
}
Demo 示例:
redis_main.cpp
#include <iostream>
#include <string>
#include "redis_handler.h"
using namespace std;
int main()
{
RedisHandler* rh = new RedisHandler();
int ret;
//连接测试
cout << "错误测试: " << "地址错误" << endl;
ret = rh->connect("34.15.14.15", 6379, "linesum");
if (ret != M_REDIS_OK)
cout << "redis error: " << rh->getErrorMsg() << endl;
cout << "错误测试: " << "端口错误" << endl;
ret = rh->connect("127.0.0.1", 1234, "linesum");
if (ret != M_REDIS_OK)
cout << "redis error: " << rh->getErrorMsg() << endl;
cout << "错误测试: " << "密码错误" << endl;
ret = rh->connect("127.0.0.1", 6479, "linsum");
if (ret != M_REDIS_OK)
cout << "redis error: " << rh->getErrorMsg() << endl;
ret = rh->connect("127.0.0.1", 6479, "linesum");
if (ret != M_REDIS_OK)
{
cout << "redis error: " << rh->getErrorMsg() << endl;
return ret;
}
//set测试
cout << "错误测试: " << "set不带value参数" << endl;
ret = rh->setValue("key11", "");
if (ret != M_REDIS_OK)
cout << "redis error: " << rh->getErrorMsg() << endl;
ret = rh->setValue("key11", "value11");
if (ret != M_REDIS_OK)
{
cout << "redis error: " << rh->getErrorMsg() << endl;
return ret;
}
ret = rh->setValue("key22", "value22");
if (ret != M_REDIS_OK)
{
cout << "redis error: " << rh->getErrorMsg() << endl;
return ret;
}
//get测试
string str;
cout << "错误测试: " << "get不带key参数" << endl;
ret = rh->getValue("key1111", str);
if (ret != M_REDIS_OK)
cout << "redis error: " << rh->getErrorMsg() << endl;
ret = rh->getValue("key11", str);
if (ret != M_REDIS_OK)
{
cout << "redis error: " << rh->getErrorMsg() << endl;
return ret;
}
else
cout << "value : " << str << endl;
//print测试
ret = rh->printAll();
if (ret != M_REDIS_OK)
{
cout << "redis error: " << rh->getErrorMsg() << endl;
return ret;
}
//del测试
cout << "错误测试: " << "删除不存在的key" << endl;
ret = rh->delKey("key1111");
if (ret != M_REDIS_OK)
cout << "redis error: " << rh->getErrorMsg() << endl;
ret = rh->delKey("key11");
if (ret != M_REDIS_OK)
{
cout << "redis error: " << rh->getErrorMsg() << endl;
return ret;
}
delete rh;
return 0;
}