運用 http://blog.csdn.net/xumaojun/article/details/51558237 中的redis_publisher.h redis_publisher.cpp redis_subscriber.h redis_subscriber.cpp四個檔案,做一個操作類進行測試. 標頭檔 Policy.h
#pragma once#include "redis_publisher.h"#include "redis_subscriber.h"#include <iostream>using namespace std;class Policy{public: Policy(); ~Policy(); void publish(); static void recieve_message(const char *channel_name,const char *message, int len); CRedisPublisher publisher; CRedisSubscriber subscriber;};代碼檔案 Policy.cpp
#include "Policy.h"Policy::Policy(){ bool ret = publisher.init(); if (!ret) { printf("Init failed.\n"); } ret = publisher.connect(); if (!ret) { printf("connect failed."); } //*********************************** CRedisSubscriber::NotifyMessageFn fn = bind(recieve_message, std::tr1::placeholders::_1, std::tr1::placeholders::_2, std::tr1::placeholders::_3); bool ret1 = subscriber.init(fn); if (!ret1) { printf("Init failed.\n"); } ret1 = subscriber.connect(); if (!ret1) { printf("Connect failed.\n"); } subscriber.subscribe("test-channel");}Policy::~Policy(){ publisher.disconnect(); publisher.uninit(); subscriber.disconnect(); subscriber.uninit();}void Policy::publish(){ cout<<"Policy::publish()...\n"<<endl; publisher.publish("test-channel", "Test message");}void Policy::recieve_message(const char *channel_name, const char *message, int len){ printf("Recieve message: channel name: %s message: %s\n", channel_name, message);}
測試檔案 testPolicy.cpp
#include "Policy.h"int main(int argc, char *argv[]){ Policy m_policy; int i=0; while(i<8){ m_policy.publish(); sleep(2); cout<<"main sleep...\n"; i++; } return 0;}
Makefile
EXE=server_main client_main CC=g++ FLAG=-lhiredis -levent -pthread OBJ=redis_publisher.o redis_subscriber.o Policy.o testPolicy.o all:$(EXE)$(EXE):$(OBJ)$(CC) -o testPolicy redis_publisher.o redis_subscriber.o Policy.o testPolicy.o $(FLAG) redis_publisher.o:redis_publisher.hredis_subscriber.o:redis_subscriber.hPolicy.o:Policy.h
這樣編譯之後,可以自發自收. 本例的用途在於可以簡單地在項目中實現異構的資料收發.
附上原作代碼: redis_publisher.h
/************************************************************************* > File Name: redis_publisher.h > Author: chenzengba > Mail: chenzengba@gmail.com > Created Time: Sat 23 Apr 2016 10:15:09 PM CST > Description: 封裝hiredis,實現訊息發布給redis功能 ************************************************************************/ #ifndef REDIS_PUBLISHER_H #define REDIS_PUBLISHER_H #include <stdlib.h> #include <hiredis/async.h> #include <hiredis/adapters/libevent.h> #include <string> #include <vector> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #include <boost/tr1/functional.hpp> class CRedisPublisher { public: CRedisPublisher(); ~CRedisPublisher(); bool init(); bool uninit(); bool connect(); bool disconnect(); bool publish(const std::string &channel_name, const std::string &message); private: // 下面三個回呼函數供redis服務調用 // 串連回調 static void connect_callback(const redisAsyncContext *redis_context, int status); // 中斷連線的回調 static void disconnect_callback(const redisAsyncContext *redis_context, int status); // 執行命令回調 static void command_callback(redisAsyncContext *redis_context, void *reply, void *privdata); // 事件分發線程函數 static void *event_thread(void *data); void *event_proc(); private: // libevent事件對象 event_base *_event_base; // 事件線程ID pthread_t _event_thread; // 事件線程的訊號量 sem_t _event_sem; // hiredis非同步對象 redisAsyncContext *_redis_context; };
redis_publisher.cpp
/************************************************************************* > File Name: redis_publisher.cpp > Author: chenzengba > Mail: chenzengba@gmail.com > Created Time: Sat 23 Apr 2016 10:15:09 PM CST > Description: ************************************************************************/ #include <stddef.h> #include <assert.h> #include <string.h> #include "redis_publisher.h" CRedisPublisher::CRedisPublisher():_event_base(0), _event_thread(0), _redis_context(0) { } CRedisPublisher::~CRedisPublisher() { } bool CRedisPublisher::init() { // initialize the event _event_base = event_base_new(); // 建立libevent對象 if (NULL == _event_base) { printf(": Create redis event failed.\n"); return false; } memset(&_event_sem, 0, sizeof(_event_sem)); int ret = sem_init(&_event_sem, 0, 0); if (ret != 0) { printf(": Init sem failed.\n"); return false; } return true; } bool CRedisPublisher::uninit() { _event_base = NULL; sem_destroy(&_event_sem); return true; } bool CRedisPublisher::connect() { // connect redis _redis_context = redisAsyncConnect("127.0.0.1", 6379); // 非同步串連到redis伺服器上,使用預設連接埠 if (NULL == _redis_context) { printf(": Connect redis failed.\n"); return false; } if (_redis_context->err) { printf(": Connect redis error: %d, %s\n", _redis_context->err, _redis_context->errstr); // 輸出錯誤資訊 return false; } // attach the event redisLibeventAttach(_redis_context, _event_base); // 將事件綁定到redis context上,使設定給redis的回調跟事件關聯 // 建立事件處理線程 int ret = pthread_create(&_event_thread, 0, &CRedisPublisher::event_thread, this); if (ret != 0) { printf(": create event thread failed.\n"); disconnect(); return false; } // 設定串連回調,當非同步呼叫串連後,伺服器處理串連請求結束後調用,通知調用者串連的狀態 redisAsyncSetConnectCallback(_redis_context, &CRedisPublisher::connect_callback); // 設定中斷連線回調,當伺服器中斷連線後,通知調用者串連斷開,調用者可以利用這個函數實現重連 redisAsyncSetDisconnectCallback(_redis_context, &CRedisPublisher::disconnect_callback); // 啟動事件線程 sem_post(&_event_sem); return true; } bool CRedisPublisher::disconnect() { if (_redis_context) { redisAsyncDisconnect(_redis_context); redisAsyncFree(_redis_context); _redis_context = NULL; } return true; } bool CRedisPublisher::publish(const std::string &channel_name, const std::string &message) { int ret = redisAsyncCommand(_redis_context, &CRedisPublisher::command_callback, this, "PUBLISH %s %s", channel_name.c_str(), message.c_str()); if (REDIS_ERR == ret) { printf("Publish command failed: %d\n", ret); return false; } return true; } void CRedisPublisher::connect_callback(const redisAsyncContext *redis_context, int status) { if (status != REDIS_OK) { printf(": Error: %s\n", redis_context->errstr); } else { printf(": Redis connected!\n"); } } void CRedisPublisher::disconnect_callback( const redisAsyncContext *redis_context, int status) { if (status != REDIS_OK) { // 這裡異常退出,可以嘗試重連 printf(": Error: %s\n", redis_context->errstr); } } // 訊息接收回呼函數 void CRedisPublisher::command_callback(redisAsyncContext *redis_context, void *reply, void *privdata) { printf("command callback.\n"); // 這裡不執行任何操作 } void *CRedisPublisher::event_thread(void *data) { if (NULL == data) { printf(": Error!\n"); assert(false); return NULL; } CRedisPublisher *self_this = reinterpret_cast<CRedisPublisher *>(data); return self_this->event_proc(); } void *CRedisPublisher::event_proc() { sem_wait(&_event_sem); // 開啟事件分發,event_base_dispatch會阻塞 event_base_dispatch(_event_base); return NULL; }
redis_subscriber.h
/************************************************************************* > File Name: redis_subscriber.h > Author: chenzengba > Mail: chenzengba@gmail.com > Created Time: Sat 23 Apr 2016 10:15:09 PM CST > Description: 封裝hiredis,實現訊息訂閱redis功能 ************************************************************************/ #ifndef REDIS_SUBSCRIBER_H #define REDIS_SUBSCRIBER_H #include <stdlib.h> #include <hiredis/async.h> #include <hiredis/adapters/libevent.h> #include <string> #include <vector> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #include <boost/tr1/functional.hpp> class CRedisSubscriber { public: typedef std::tr1::function<void (const char *, const char *, int)> NotifyMessageFn; // 回呼函數物件類型,當接收到訊息後調用回調把訊息發送出去 CRedisSubscriber(); ~CRedisSubscriber(); bool init(const NotifyMessageFn &fn); // 傳入回調對象 bool uninit(); bool connect(); bool disconnect(); // 可以多次調用,訂閱多個頻道 bool subscribe(const std::string &channel_name); private: // 下面三個回呼函數供redis服務調用 // 串連回調 static void connect_callback(const redisAsyncContext *redis_context, int status); // 中斷連線的回調 static void disconnect_callback(const redisAsyncContext *redis_context, int status); // 執行命令回調 static void command_callback(redisAsyncContext *redis_context, void *reply, void *privdata); // 事件分發線程函數 static void *event_thread(void *data); void *event_proc(); private: // libevent事件對象 event_base *_event_base; // 事件線程ID pthread_t _event_thread; // 事件線程的訊號量 sem_t _event_sem; // hiredis非同步對象 redisAsyncContext *_redis_context; // 通知外層的回呼函數對象 NotifyMessageFn _notify_message_fn; };
redis_subscriber.cpp
/************************************************************************* > File Name: redis_subscriber.cpp > Author: chenzengba > Mail: chenzengba@gmail.com > Created Time: Sat 23 Apr 2016 10:15:09 PM CST > Description: ************************************************************************/ #include <stddef.h> #include <assert.h> #include <string.h> #include "redis_subscriber.h" CRedisSubscriber::CRedisSubscriber():_event_base(0), _event_thread(0), _redis_context(0) { } CRedisSubscriber::~CRedisSubscriber() { } bool CRedisSubscriber::init(const NotifyMessageFn &fn) { // initialize the event _notify_message_fn = fn; _event_base = event_base_new(); // 建立libevent對象 if (NULL == _event_base) { printf(": Create redis event failed.\n"); return false; } memset(&_event_sem, 0, sizeof(_event_sem)); int ret = sem_init(&_event_sem, 0, 0); if (ret != 0) { printf(": Init sem failed.\n"); return false; } return true; } bool CRedisSubscriber::uninit() { _event_base = NULL; sem_destroy(&_event_sem); return true; } bool CRedisSubscriber::connect() { // connect redis _redis_context = redisAsyncConnect("127.0.0.1", 6379); // 非同步串連到redis伺服器上,使用預設連接埠 if (NULL == _redis_context) { printf(": Connect redis failed.\n"); return false; } if (_redis_context->err) { printf(": Connect redis error: %d, %s\n", _redis_context->err, _redis_context->errstr); // 輸出錯誤資訊 return false; } // attach the event redisLibeventAttach(_redis_context, _event_base); // 將事件綁定到redis context上,使設定給redis的回調跟事件關聯 // 建立事件處理線程 int ret = pthread_create(&_event_thread, 0, &CRedisSubscriber::event_thread, this); if (ret != 0) { printf(": create event thread failed.\n"); disconnect(); return false; } // 設定串連回調,當非同步呼叫串連後,伺服器處理串連請求結束後調用,通知調用者串連的狀態 redisAsyncSetConnectCallback(_redis_context, &CRedisSubscriber::connect_callback); // 設定中斷連線回調,當伺服器中斷連線後,通知調用者串連斷開,調用者可以利用這個函數實現重連 redisAsyncSetDisconnectCallback(_redis_context, &CRedisSubscriber::disconnect_callback); // 啟動事件線程 sem_post(&_event_sem); return true; } bool CRedisSubscriber::disconnect() { if (_redis_context) { redisAsyncDisconnect(_redis_context); redisAsyncFree(_redis_context); _redis_context = NULL; } return true; } bool CRedisSubscriber::subscribe(const std::string &channel_name) { int ret = redisAsyncCommand(_redis_context, &CRedisSubscriber::command_callback, this, "SUBSCRIBE %s", channel_name.c_str()); if (REDIS_ERR == ret) { printf("Subscribe command failed: %d\n", ret); return false; } printf(": Subscribe success: %s\n", channel_name.c_str()); return true; } void CRedisSubscriber::connect_callback(const redisAsyncContext *redis_context, int status) { if (status != REDIS_OK) { printf(": Error: %s\n", redis_context->errstr); } else { printf(": Redis connected!"); } } void CRedisSubscriber::disconnect_callback( const redisAsyncContext *redis_context, int status) { if (status != REDIS_OK) { // 這裡異常退出,可以嘗試重連 printf(": Error: %s\n", redis_context->errstr); } } // 訊息接收回呼函數 void CRedisSubscriber::command_callback(redisAsyncContext *redis_context, void *reply, void *privdata) { if (NULL == reply || NULL == privdata) { return ; } // 靜態函數中,要使用類的成員變數,把當前的this指標傳進來,用this指標間接訪問 CRedisSubscriber *self_this = reinterpret_cast<CRedisSubscriber *>(privdata); redisReply *redis_reply = reinterpret_cast<redisReply *>(reply); // 訂閱接收到的訊息是一個帶三元素的數組 if (redis_reply->type == REDIS_REPLY_ARRAY && redis_reply->elements == 3) { printf(": Recieve message:%s:%d:%s:%d:%s:%d\n", redis_reply->element[0]->str, redis_reply->element[0]->len, redis_reply->element[1]->str, redis_reply->element[1]->len, redis_reply->element[2]->str, redis_reply->element[2]->len); // 調用函數對象把訊息通知給外層 self_this->_notify_message_fn(redis_reply->element[1]->str, redis_reply->element[2]->str, redis_reply->element[2]->len); } } void *CRedisSubscriber::event_thread(void *data) { if (NULL == data) { printf(": Error!\n"); assert(false); return NULL; } CRedisSubscriber *self_this = reinterpret_cast<CRedisSubscriber *>(data); return self_this->event_proc(); } void *CRedisSubscriber::event_proc() { sem_wait(&_event_sem); // 開啟事件分發,event_base_dispatch會阻塞 event_base_dispatch(_event_base); return NULL; }