• 封装hiredis——C++与redis对接(一)(string的SET与GET操作)


      在菜鸟教程自学了redis,总想着像Mysql一样,在C/C++中进行对接。于是查询了一些资料,最后找到了hiredis。然而直接用它的话,难免有点不方便。于是,对其进行封装。

      hiredis直接去git上克隆,地址:https://github.com/redis/hiredis。

      下载好之后,由于其自带Makefile,只要make一下就编译出静态库与动态库了,接着把头文件和静/动态库放在相应的文件夹里就可以了。注意如果使用动态库,而且是放在/usr/local/lib/里,得执行ldconfig命令,来更新一下配置,或者得配置一下动态库路径。

      安装好了就是如何使用的事了。

      学习hiredis主要是参考这两个链接:

      http://blog.csdn.net/gqtcgq/article/details/51344232

      http://blog.csdn.net/achelloworld/article/details/41598389?utm_source=tuicool&utm_medium=referral

      一共就五个函数。

    1、redisContext* redisConnect(const char *ip, int port)

    2、redisContext* redisConnectWithTimeout(const char *ip, int port, timeval tv)

    3、void redisFree(redisContext *c)

    4、void *redisCommand(redisContext *c, const char *format...)

    5、void freeReplyObject(void *reply)

      和Mysql一样,要对接,第一件事就是用IP和端口号建立连接什么的。redis的端口号一般是6379,IP直接用127.0.0.1就可以了。既然要用到IP和端口号,又是可能会变的东西,为了不使想要改变它们的时候得直接修改代码,我写了个配置文件:

    redisConf.json

    1 {
    2     "IP" : "127.0.0.1" ,
    3     "PORT" : 6379
    4 }

      相应地,有提取配置信息的类

    redisConf.h

     1 #ifndef __REDISCONF_H__
     2 #define __REDISCONF_H__
     3 #include <string>
     4 namespace ccx{
     5 using std::string;
     6 class RedisConf
     7 {
     8     public:
     9         RedisConf();
    10         void getConf();
    11         string getIP();
    12         int getPort();
    13     private:
    14         string _ip;
    15         int _port;
    16 };
    17 }
    18 #endif

    redisconf.cc

     1 #include "redisConf.h"
     2 #include <stdlib.h>
     3 #include <json/json.h>
     4 #include <string>
     5 #include <iostream>
     6 #include <fstream>
     7 
     8 namespace ccx{
     9 
    10 using std::ifstream;
    11 using std::cout;
    12 using std::endl;
    13 
    14 RedisConf::RedisConf()
    15 {
    16     getConf();
    17 }
    18 
    19 void RedisConf::getConf()
    20 {
    21     ifstream ifs;
    22     ifs.open("redisConf.json");
    23     if(!ifs.good())
    24     {
    25         cout << "open RedisConf.json error" << endl;
    26         exit(EXIT_FAILURE);
    27     }
    28 
    29     Json::Value root;
    30     Json::Reader reader;
    31     if(!reader.parse(ifs, root, false))
    32     {
    33         cout << "RedisConf json reader error" << endl;
    34         exit(EXIT_FAILURE);
    35     }
    36     
    37     _ip = root["IP"].asString();
    38     _port = root["PORT"].asInt();
    39     ifs.close();
    40 }
    41 
    42 string RedisConf::getIP()
    43 {
    44     return _ip;
    45 }
    46 
    47 int RedisConf::getPort()
    48 {
    49     return _port;
    50 }
    51 
    52 }

      

      然后是目前的redis类:

    redis.h

     1 #ifndef __REDIS_H__
     2 #define __REDIS_H__
     3 
     4 #include "redisConf.h"
     5 
     6 #include <hiredis/hiredis.h>
     7 
     8 
     9 namespace ccx{
    10 
    11 class Redis
    12 {
    13     public:
    14         Redis();
    15     public:
    16         void Connect();
    17         void disConnect();
    18     public:
    19         void setString(const string & key, const string & value);
    20         void setString(const string & key, const int & value);
    21         void setString(const string & key, const float & value);
    22     private:
    23         void setString(const string & data);
    24     public:
    25         void getString(const string & key, string & value);
    26         void getString(const string & key, int & value);
    27         void getString(const string & key, float & value);
    28     private:
    29         void getString(const string & key);
    30     private:
    31         void freeReply();
    32         bool isError();
    33     private:
    34         RedisConf _conf;
    35         redisContext * _context;
    36         redisReply * _reply;
    37 };
    38 }
    39 
    40 #endif

      

      下面结合写好的代码说说前面的五个函数。

      函数1是用来连接redis的,具体如下:

     1 void Redis::Connect()
     2 {
     3     _context = ::redisConnect(_conf.getIP().c_str(), _conf.getPort());
     4     cout << _conf.getIP() << "-" << _conf.getPort() << endl;
     5     if(_context && _context->err)
     6     {
     7         cout << "connect redis error" << endl;
     8         exit(EXIT_FAILURE);    
     9     }
    10     cout << "redis Connect success" << endl;
    11 }

      

      函数2是在1的基础上,添加了一个超时功能。

      函数3是在不使用redis了,要断开连接时使用的:

    1 void Redis::disConnect()
    2 {
    3     ::redisFree(_context);
    4     cout << "redis disConnect success" << endl;
    5 }

      

      函数4稍微复杂一些,有点像C中的printf:

    1 printf("%d%s%d",d1,s1,d2);
    2 printf("hello,world");

      

      可以这样用:

    1 char * command = "SET name lili";
    2 reply = (redisReply*)::redisCommand(context, command);
    3 char * s1 = "name";
    4 char * s2 = "lili";
    5 reply = (redisReply*)::redisCommand(context, "SET %s %s", s1, s2);
    6 reply = (redisReply*)::redisCommand(context, "SET name lili");
    7 ...

      第一个参数context是函数1或者2的返回值,告诉它要与哪里的redis进行交互。reply指向命令结果的存储位置。

      函数5是用来清理函数4 的返回结果的:

    1 void Redis::freeReply()
    2 {
    3     if(_reply)
    4     {
    5         ::freeReplyObject(_reply);
    6         _reply = NULL;
    7     }
    8 }

      第6行是因为对这个函数不熟,就干脆清完之后给它赋值NULL。

      由于redis的string中存的可能是字符串、整形、浮点数,于是各自重载了三个版本的get与set方法,并重用一些函数,以减少代码量。

      对于set,直接用一个宏替换:

    1 #define SETSTRING(key, value) 
    2     stringstream ss;
    3     ss << "SET " << key << " " << value;
    4     string s;
    5     getline(ss, s);
    6     setString(s);
     1 void Redis::setString(const string & key, const string & value)
     2 {
     3     SETSTRING(key, value);
     4 }
     5 void Redis::setString(const string & key, const int & value)
     6 {
     7     SETSTRING(key, value);
     8 }
     9 void Redis::setString(const string & key, const float & value)
    10 {
    11     SETSTRING(key, value);
    12 }

      使用C++中的stringstream,会比用“%d”、“%s”、“%f”来区分类型少些代码。两种方法的结果是相同的。

      它们共用的setString方法:

     1 void Redis::setString(const string & data)
     2 {
     3     freeReply();
     4     _reply = (redisReply*)::redisCommand(_context, data.c_str());
     5     if(!isError())
     6     {
     7         if (!(_reply->type == REDIS_REPLY_STATUS && strcasecmp(_reply->str,"OK") == 0))
     8         {
     9             cout << "Failed to execute SET(string)" << endl;
    10         }    
    11     }
    12 }

      这里的isError是用来判断是否连接异常的:

     1 bool Redis::isError()
     2 {
     3     if(NULL == _reply)
     4     {
     5         freeReply();
     6         disConnect();
     7         Connect();
     8         return true;
     9     }
    10     return false;
    11 }

      如果连接异常,得断开重连。

      在redis命令行里,如果set成功,会提示“OK”。于是,这里先判断了一下命令结果的数据类型,如果是字符串,再判断它是不是“OK”,以此来判断set是否成功。

      对于get,我试了各种方法,都无法直接从命令结果中提取出数字,暂时还没找到原因。但是数字却可以以字符串格式得到。于是,使用了atoi来处理:

     1 void Redis::getString(const string & key)
     2 {
     3     freeReply();
     4     _reply = (redisReply*)::redisCommand(_context, "GET %s", key.c_str());
     5 }
     6 
     7 void Redis::getString(const string & key, string & value)
     8 {
     9     getString(key);
    10     if(!isError() && _reply->type == REDIS_REPLY_STRING)
    11     {
    12         value = _reply->str;
    13     }
    14 }
    15 
    16 void Redis::getString(const string & key, int & value)
    17 {
    18     getString(key);
    19     if(!isError() && _reply->type == REDIS_REPLY_STRING)
    20     {
    21         value = ::atoi(_reply->str);
    22     }
    23 }
    24 
    25 void Redis::getString(const string & key, float & value)
    26 {
    27     getString(key);
    28     if(!isError() && _reply->type == REDIS_REPLY_STRING)
    29     {
    30         value = ::atof(_reply->str);
    31     }
    32 }

    redis.cc

      1 #include "redis.h"
      2 
      3 #include <string.h>
      4 #include <stdlib.h>
      5 
      6 #include <sstream>
      7 #include <iostream>
      8 
      9 namespace ccx{
     10 
     11 using std::cout;
     12 using std::endl;
     13 using std::stringstream;
     14 
     15 #define SETSTRING(key, value) 
     16     stringstream ss;
     17     ss << "SET " << key << " " << value;
     18     string s;
     19     getline(ss, s);
     20     setString(s);
     21 
     22 Redis::Redis()
     23 : _conf()
     24 {
     25 }
     26 
     27 void Redis::Connect()
     28 {
     29     _context = ::redisConnect(_conf.getIP().c_str(), _conf.getPort());
     30     cout << _conf.getIP() << "-" << _conf.getPort() << endl;
     31     if(_context && _context->err)
     32     {
     33         cout << "connect redis error" << endl;
     34         exit(EXIT_FAILURE);    
     35     }
     36     cout << "redis Connect success" << endl;
     37 }
     38 
     39 void Redis::disConnect()
     40 {
     41     ::redisFree(_context);
     42     cout << "redis disConnect success" << endl;
     43 }
     44 
     45 void Redis::setString(const string & data)
     46 {
     47     freeReply();
     48     _reply = (redisReply*)::redisCommand(_context, data.c_str());
     49     if(!isError())
     50     {
     51         if (!(_reply->type == REDIS_REPLY_STATUS && strcasecmp(_reply->str,"OK") == 0))
     52         {
     53             cout << "Failed to execute SET(string)" << endl;
     54         }    
     55     }
     56 }
     57 
     58 void Redis::setString(const string & key, const string & value)
     59 {
     60     SETSTRING(key, value);
     61 }
     62 
     63 void Redis::setString(const string & key, const int & value)
     64 {
     65     SETSTRING(key, value);
     66 }
     67 
     68 void Redis::setString(const string & key, const float & value)
     69 {
     70     SETSTRING(key, value);
     71 }
     72 
     73 void Redis::getString(const string & key)
     74 {
     75     freeReply();
     76     _reply = (redisReply*)::redisCommand(_context, "GET %s", key.c_str());
     77 }
     78 
     79 void Redis::getString(const string & key, string & value)
     80 {
     81     getString(key);
     82     if(!isError() && _reply->type == REDIS_REPLY_STRING)
     83     {
     84         value = _reply->str;
     85     }
     86 }
     87 
     88 void Redis::getString(const string & key, int & value)
     89 {
     90     getString(key);
     91     if(!isError() && _reply->type == REDIS_REPLY_STRING)
     92     {
     93         value = ::atoi(_reply->str);
     94     }
     95 }
     96 
     97 void Redis::getString(const string & key, float & value)
     98 {
     99     getString(key);
    100     if(!isError() && _reply->type == REDIS_REPLY_STRING)
    101     {
    102         value = ::atof(_reply->str);
    103     }
    104 }
    105 
    106 void Redis::freeReply()
    107 {
    108     if(_reply)
    109     {
    110         ::freeReplyObject(_reply);
    111         _reply = NULL;
    112     }
    113 }
    114 
    115 bool Redis::isError()
    116 {
    117     if(NULL == _reply)
    118     {
    119         freeReply();
    120         disConnect();
    121         Connect();
    122         return true;
    123     }
    124     return false;
    125 }
    126 
    127 }
    View Code

    test.cc

     1 #include "redis.h"
     2 
     3 #include <string>
     4 #include <iostream>
     5 
     6 using std::cout;
     7 using std::endl;
     8 using std::string;
     9 
    10 int main()
    11 {
    12     ccx::Redis redis;
    13     redis.Connect();
    14     redis.setString("name", "lii");
    15 
    16     string s;
    17     redis.getString("name", s);
    18     cout << s << endl;
    19 
    20     redis.setString("age", "30");
    21     redis.getString("age", s);
    22     cout << s << endl;
    23     
    24     int i;
    25     redis.getString("age", i);
    26     cout << i << endl;    
    27 
    28     redis.disConnect();
    29 }
    View Code

      测试结果如下:

    127.0.0.1-6379
    redis Connect success
    lii
    30
    30
    redis disConnect success
  • 相关阅读:
    Windows光标形状
    函数对象(仿函数 functor)
    构造函数的初始化列表抛出异常
    <<Windows via C/C++>>学习笔记 —— 线程优先级【转】
    单例模式
    c++中的重载(Overload)、覆盖(重写,Override) 、隐藏与访问权限控制及using声明
    RTTI: dynamic_cast typeid
    抽象类 虚函数 声明与实现
    typedef 函数指针 数组 std::function
    Client Window坐标 RECT相关函数
  • 原文地址:https://www.cnblogs.com/chinxi/p/6184885.html
Copyright © 2020-2023  润新知