목차
基于Hiredis异步API的聊天系统实现
데이터 베이스 MySQL 튜토리얼 基于Hiredis异步API的聊天系统实现

基于Hiredis异步API的聊天系统实现

Jun 07, 2016 pm 02:51 PM
api 기반으로 성취하다 비동기식 체계 채팅

基于Hiredis异步API的聊天系统实现 上一篇文章http://blog.csdn.net/qq_34788352/article/details/51313027使用Hiredis的同步API实现了发送消息的客户端,当我使用同步API实现订阅频道客户端时,一旦订阅频道,就会出现无法操作的情况,这是就是同步和异步的

基于Hiredis异步API的聊天系统实现

上一篇文章http://blog.csdn.net/qq_34788352/article/details/51313027使用Hiredis的同步API实现了发送消息的客户端,当我使用同步API实现订阅频道客户端时,一旦订阅频道,就会出现无法操作的情况,这是就是同步和异步的问题。使用同步API,订阅频道后,客户端会进入阻塞状态,等待订阅频道发布的消息,不能实现既订阅频道,又能发布消息的功能。为了实现一个客户端既能订阅频道,又能发布消息的功能,就需要使用Hiredis的异步API。

首先特别感谢黄天霸、tickTick、vah101,当我遇到各种各样奇怪问题的时候,你们的帖子给与了我解答,谢谢。

开始正题,hiredis异步的实现主要是依靠redis自带的ae事件库或者libev事件库或者libevent的事件库或者libuv事件库。网上一些人是通过libevent事件库来实现,本系统则使用Redis自带ae事件库来实现,Redis不用libevent事件库而选择重写一个ae事件库,必定有其道理。

首先介绍使用到的异步API,位于async.h中:

<code class=" hljs perl">redisAsyncContext <span class="hljs-variable">*redisAsyncConnect</span>(const char <span class="hljs-variable">*ip</span>, <span class="hljs-keyword">int</span> port); <span class="hljs-regexp">//</span>用于建立异步连接

<span class="hljs-keyword">int</span> redisAsyncSetConnectCallback(redisAsyncContext <span class="hljs-variable">*ac</span>, redisConnectCallback <span class="hljs-variable">*fn</span>);  <span class="hljs-regexp">//</span>设置连接回调函数,回调函数形式:void callback(const redisAsyncContext <span class="hljs-variable">*c</span>, <span class="hljs-keyword">int</span> status);

<span class="hljs-keyword">int</span> redisAsyncSetDisconnectCallback(redisAsyncContext <span class="hljs-variable">*ac</span>, redisDisconnectCallback <span class="hljs-variable">*fn</span>);  <span class="hljs-regexp">//</span>设置断开连接回调函数,回调函数形式:void callback(const redisAsyncContext <span class="hljs-variable">*c</span>, <span class="hljs-keyword">int</span> status);

void redisAsyncDisconnect(redisAsyncContext <span class="hljs-variable">*ac</span>); <span class="hljs-regexp">//</span>断开异步连接

void redisAsyncFree(redisAsyncContext <span class="hljs-variable">*ac</span>); <span class="hljs-regexp">//</span>释放建立连接时,创建的redisAsyncContext结构

<span class="hljs-keyword">int</span> redisAsyncCommand(redisAsyncContext <span class="hljs-variable">*ac</span>, redisCallbackFn <span class="hljs-variable">*fn</span>, void <span class="hljs-variable">*privdata</span>, const char <span class="hljs-variable">*format</span>, ...); <span class="hljs-regexp">//</span>发送Redis命令,需要实现一个回调函数来出来命令的返回,fn是回调函数的地址,回调函数形式:void callback(redisAsyncContext <span class="hljs-variable">*c</span>, void <span class="hljs-variable">*reply</span>, void <span class="hljs-variable">*pridata</span>);
</code>
로그인 후 복사

有了上面的异步API,就可以开始客户端的撰写。
首先封装订阅端

<code class=" hljs cpp"><span class="hljs-comment">//sub_client.h</span>

<span class="hljs-preprocessor">#ifndef REDIS_SUB_CLIENT_H  </span>
<span class="hljs-preprocessor">#define REDIS_SUB_CLIENT_H  </span>

<span class="hljs-keyword">extern</span> <span class="hljs-string">"C"</span>
{  
<span class="hljs-preprocessor">#include <stdlib.h>  </span>
<span class="hljs-preprocessor">#include <async.h>  </span>
<span class="hljs-preprocessor">#include <adapters/ae.h> </span>
<span class="hljs-preprocessor">#include <unistd.h>  </span>
<span class="hljs-preprocessor">#include <pthread.h>  </span>
<span class="hljs-preprocessor">#include <semaphore.h> </span>
} 

<span class="hljs-preprocessor">#include <string>  </span>
<span class="hljs-preprocessor">#include <vector>  </span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;

<span class="hljs-keyword">class</span> CRedisSubClient  
{  
<span class="hljs-keyword">public</span>:   

    CRedisSubClient();  
    ~CRedisSubClient();  

    <span class="hljs-keyword">bool</span> init();   <span class="hljs-comment">//初始化,事件对象,信号量  </span>
    <span class="hljs-keyword">bool</span> uninit();  <span class="hljs-comment">//释放对象</span>
    <span class="hljs-keyword">bool</span> connect();  <span class="hljs-comment">//连接服务器</span>
    <span class="hljs-keyword">bool</span> disconnect(); <span class="hljs-comment">//断开服务器</span>

    <span class="hljs-comment">//订阅频道 </span>
    <span class="hljs-keyword">bool</span> subscribe(<span class="hljs-keyword">const</span> <span class="hljs-built_in">string</span> &channel_name);  

<span class="hljs-keyword">private</span>:  
    <span class="hljs-comment">// 下面三个回调函数供redis服务调用  </span>
    <span class="hljs-comment">// 连接回调  </span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> connect_callback(<span class="hljs-keyword">const</span> redisAsyncContext *redis_context,  
        <span class="hljs-keyword">int</span> status);  

    <span class="hljs-comment">// 断开连接的回调  </span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> disconnect_callback(<span class="hljs-keyword">const</span> redisAsyncContext *redis_context,  
        <span class="hljs-keyword">int</span> status);  

    <span class="hljs-comment">// 执行命令回调  </span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> command_callback(redisAsyncContext *redis_context,  
        <span class="hljs-keyword">void</span> *reply, <span class="hljs-keyword">void</span> *privdata);  

    <span class="hljs-comment">// 事件分发线程函数  </span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> *event_thread(<span class="hljs-keyword">void</span> *data);  
    <span class="hljs-keyword">void</span> *event_proc();  

<span class="hljs-keyword">private</span>:  
    <span class="hljs-comment">// ae事件对象  </span>
    aeEventLoop *loop;  
    <span class="hljs-comment">// 事件线程ID  </span>
    pthread_t _event_thread;  
    <span class="hljs-comment">// 事件线程的信号量  </span>
    sem_t _event_sem;  
    <span class="hljs-comment">// hiredis异步对象  </span>
    redisAsyncContext *_redis_context;  

};  

<span class="hljs-preprocessor">#endif  </span>
</code>
로그인 후 복사

使用extern “C”是因为redis和hiredis都是c写的,当使用c++链接c代码生成的库中的函数时,会出现undefined reference的问题。

订阅端的实现

<code class=" hljs cpp"><span class="hljs-comment">//sub_client.cpp</span>

<span class="hljs-preprocessor">#include <stddef.h>  </span>
<span class="hljs-preprocessor">#include <assert.h>  </span>
<span class="hljs-preprocessor">#include <string.h>  </span>
<span class="hljs-preprocessor">#include "sub_client.h" </span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>; 

CRedisSubClient::CRedisSubClient():loop(<span class="hljs-number">0</span>), _event_thread(<span class="hljs-number">0</span>),  
_redis_context(<span class="hljs-number">0</span>)  
{  
}  

CRedisSubClient::~CRedisSubClient()  
{  
}  

<span class="hljs-keyword">bool</span> CRedisSubClient::init()  
{  

    loop = aeCreateEventLoop(<span class="hljs-number">64</span>);    <span class="hljs-comment">// 创建ae对象</span>

    <span class="hljs-keyword">if</span> (NULL == loop)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Create redis event failed.\n"</span>);  
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    <span class="hljs-built_in">memset</span>(&_event_sem, <span class="hljs-number">0</span>, <span class="hljs-keyword">sizeof</span>(_event_sem)); 

    <span class="hljs-keyword">int</span> ret = sem_init(&_event_sem, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);  <span class="hljs-comment">//初始化线程信号量</span>

    <span class="hljs-keyword">if</span> (ret != <span class="hljs-number">0</span>)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Init sem failed.\n"</span>);  
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;  
}  

<span class="hljs-keyword">bool</span> CRedisSubClient::uninit()  
{  
    loop = NULL;  

    sem_destroy(&_event_sem);  

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;  
}  

<span class="hljs-keyword">bool</span> CRedisSubClient::connect()  
{    
    _redis_context = redisAsyncConnect(<span class="hljs-string">"127.0.0.1"</span>, <span class="hljs-number">6379</span>);    <span class="hljs-comment">// 异步连接到redis服务器上,使用6380端口</span>

    <span class="hljs-keyword">if</span> (NULL == _redis_context)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">": Connect redis failed.\n"</span>);  
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    <span class="hljs-keyword">if</span> (_redis_context->err)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Connect redis error: %d, %s\n"</span>,   
            _redis_context->err, _redis_context->errstr);    <span class="hljs-comment">// 输出错误信息  </span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    redisAeAttach(loop,_redis_context);   <span class="hljs-comment">// 将事件绑定到redis context上,使redis的回调跟事件关联  </span>

    <span class="hljs-comment">// 创建事件处理线程  </span>
    <span class="hljs-keyword">int</span> ret = pthread_create(&_event_thread, <span class="hljs-number">0</span>, &CRedisSubscriber::event_thread, <span class="hljs-keyword">this</span>);  

    <span class="hljs-keyword">if</span> (ret != <span class="hljs-number">0</span>)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Create event thread failed.\n"</span>);  
        disconnect();  
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    <span class="hljs-comment">// 设置连接回调,当异步调用连接后,服务器处理连接请求结束后调用,通知调用者连接的状态  </span>
    redisAsyncSetConnectCallback(_redis_context,   
        &CRedisSubClient::connect_callback);  

    <span class="hljs-comment">// 设置断开连接回调,当服务器断开连接后,通知调用者连接断开,调用者可以利用这个函数实现重连  </span>
    redisAsyncSetDisconnectCallback(_redis_context,  
        &CRedisSubClient::disconnect_callback);  

    <span class="hljs-comment">// 启动事件线程  </span>
    sem_post(&_event_sem);  
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;  
}  

<span class="hljs-keyword">bool</span> CRedisSubClient::disconnect()  
{  
    <span class="hljs-keyword">if</span> (_redis_context)  
    {  
        redisAsyncDisconnect(_redis_context);  
        redisAsyncFree(_redis_context);  
        _redis_context = NULL;  
    }  

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;  
}  

<span class="hljs-keyword">bool</span> CRedisSubClient::subscribe(<span class="hljs-keyword">const</span> <span class="hljs-built_in">string</span> &channel_name)  
{  
    <span class="hljs-keyword">int</span> ret = redisAsyncCommand(_redis_context,   
        &CRedisSubscriber::command_callback, <span class="hljs-keyword">this</span>, <span class="hljs-string">"SUBSCRIBE %s"</span>,   
        channel_name.c_str()); <span class="hljs-comment">//订阅一个频道</span>

    <span class="hljs-keyword">if</span> (REDIS_ERR == ret)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Subscribe command failed: %d\n"</span>, ret);  
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Subscribe success: %s\n"</span>, channel_name.c_str());  
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;  
}  

<span class="hljs-keyword">void</span> CRedisSubClient::connect_callback(<span class="hljs-keyword">const</span> redisAsyncContext *redis_context,  
    <span class="hljs-keyword">int</span> status)  
{  
    <span class="hljs-keyword">if</span> (status != REDIS_OK)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Error: %s\n"</span>, redis_context->errstr);  
    }  
    <span class="hljs-keyword">else</span>  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Redis connected!"</span>);  
    }  
}  

<span class="hljs-keyword">void</span> CRedisSubClient::disconnect_callback(  
    <span class="hljs-keyword">const</span> redisAsyncContext *redis_context, <span class="hljs-keyword">int</span> status)  
{  
    <span class="hljs-keyword">if</span> (status != REDIS_OK)  
    {    
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">": Error: %s\n"</span>, redis_context->errstr);  
    }  
}  

<span class="hljs-comment">// 消息接收回调函数  </span>
<span class="hljs-keyword">void</span> CRedisSubClient::command_callback(redisAsyncContext *redis_context,  
    <span class="hljs-keyword">void</span> *reply, <span class="hljs-keyword">void</span> *privdata)  
{  
    <span class="hljs-keyword">if</span> (NULL == reply || NULL == privdata) {  
        <span class="hljs-keyword">return</span> ;  
    }  

    redisReply *redis_reply = <span class="hljs-keyword">reinterpret_cast</span><redisReply *>(reply);  

    <span class="hljs-comment">// 订阅接收到的消息是一个带三元素的数组  </span>
    <span class="hljs-keyword">if</span> (redis_reply->type == REDIS_REPLY_ARRAY &&  
    redis_reply->elements == <span class="hljs-number">3</span>)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Recieve message:%s %s %s\n"</span>,  
        redis_reply->element[<span class="hljs-number">0</span>]->str  
        redis_reply->element[<span class="hljs-number">1</span>]->str  
        redis_reply->element[<span class="hljs-number">2</span>]->str);    
    }  
}  

<span class="hljs-keyword">void</span> *CRedisSubClient::event_thread(<span class="hljs-keyword">void</span> *data)  
{  
    <span class="hljs-keyword">if</span> (NULL == data)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">": Error!\n"</span>);  
        assert(<span class="hljs-keyword">false</span>);  
        <span class="hljs-keyword">return</span> NULL;  
    }  

    CRedisSubClient *self_this = <span class="hljs-keyword">reinterpret_cast</span><CRedisSubClient *>(data);  
    <span class="hljs-keyword">return</span> self_this->event_proc();  
}  

<span class="hljs-keyword">void</span> *CRedisSubClient::event_proc()  
{  
    sem_wait(&_event_sem);  

    <span class="hljs-comment">//进行事件处理循环  </span>
    aeMain(loop);  

    <span class="hljs-keyword">return</span> NULL;  
}  </code>
로그인 후 복사

发布端的封装:

<code class=" hljs cpp"><span class="hljs-comment">//pub_client.h</span>

<span class="hljs-preprocessor">#ifndef REDIS_PUB_CLIENT_H  </span>
<span class="hljs-preprocessor">#define REDIS_PUB_CLIENT_H  </span>

<span class="hljs-keyword">extern</span> <span class="hljs-string">"C"</span>
{  
<span class="hljs-preprocessor">#include <stdlib.h>  </span>
<span class="hljs-preprocessor">#include <async.h>  </span>
<span class="hljs-preprocessor">#include <adapters/ae.h> </span>
<span class="hljs-preprocessor">#include <unistd.h>  </span>
<span class="hljs-preprocessor">#include <pthread.h>  </span>
<span class="hljs-preprocessor">#include <semaphore.h> </span>
} 

<span class="hljs-preprocessor">#include <string>  </span>
<span class="hljs-preprocessor">#include <vector>  </span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;

<span class="hljs-keyword">class</span> CRedisPubClient  
{  
<span class="hljs-keyword">public</span>:   

    CRedisPubClient();  
    ~CRedisPubClient();  

    <span class="hljs-keyword">bool</span> init();   <span class="hljs-comment">//初始化,事件对象,信号量  </span>
    <span class="hljs-keyword">bool</span> uninit();  <span class="hljs-comment">//释放对象</span>
    <span class="hljs-keyword">bool</span> connect();  <span class="hljs-comment">//连接服务器</span>
    <span class="hljs-keyword">bool</span> disconnect();  <span class="hljs-comment">//断开服务器</span>

    <span class="hljs-comment">//订阅频道 </span>
    <span class="hljs-keyword">bool</span> publish(<span class="hljs-keyword">const</span> <span class="hljs-built_in">string</span> &channel_name, <span class="hljs-keyword">const</span> <span class="hljs-built_in">string</span> &message);  

<span class="hljs-keyword">private</span>:  
    <span class="hljs-comment">// 下面三个回调函数供redis服务调用  </span>
    <span class="hljs-comment">// 连接回调  </span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> connect_callback(<span class="hljs-keyword">const</span> redisAsyncContext *redis_context,  
        <span class="hljs-keyword">int</span> status);  

    <span class="hljs-comment">// 断开连接的回调  </span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> disconnect_callback(<span class="hljs-keyword">const</span> redisAsyncContext *redis_context,  
        <span class="hljs-keyword">int</span> status);  

    <span class="hljs-comment">// 执行命令回调  </span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> command_callback(redisAsyncContext *redis_context,  
        <span class="hljs-keyword">void</span> *reply, <span class="hljs-keyword">void</span> *privdata);  

    <span class="hljs-comment">// 事件分发线程函数  </span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> *event_thread(<span class="hljs-keyword">void</span> *data);  
    <span class="hljs-keyword">void</span> *event_proc();  

<span class="hljs-keyword">private</span>:  
    <span class="hljs-comment">// ae事件对象  </span>
    aeEventLoop *loop;  
    <span class="hljs-comment">// 事件线程ID  </span>
    pthread_t _event_thread;  
    <span class="hljs-comment">// 事件线程的信号量  </span>
    sem_t _event_sem;  
    <span class="hljs-comment">// hiredis异步对象  </span>
    redisAsyncContext *_redis_context;  

};  

<span class="hljs-preprocessor">#endif  </span></code>
로그인 후 복사

发布端的实现:

<code class=" hljs cpp"><span class="hljs-comment">//pub_client.cpp</span>

<span class="hljs-preprocessor">#include <stddef.h>  </span>
<span class="hljs-preprocessor">#include <assert.h>  </span>
<span class="hljs-preprocessor">#include <string.h>  </span>
<span class="hljs-preprocessor">#include "pub_client.h" </span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>; 

CRedisPubClient::CRedisPubClient():loop(<span class="hljs-number">0</span>), _event_thread(<span class="hljs-number">0</span>),  
_redis_context(<span class="hljs-number">0</span>)  
{  
}  

CRedisPubClient::~CRedisPubClient()  
{  
}  

<span class="hljs-keyword">bool</span> CRedisPubClient::init()  
{  

    loop = aeCreateEventLoop(<span class="hljs-number">64</span>);    <span class="hljs-comment">// 创建ae对象</span>

    <span class="hljs-keyword">if</span> (NULL == loop)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Create redis event failed.\n"</span>);  
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    <span class="hljs-built_in">memset</span>(&_event_sem, <span class="hljs-number">0</span>, <span class="hljs-keyword">sizeof</span>(_event_sem)); 

    <span class="hljs-keyword">int</span> ret = sem_init(&_event_sem, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);  <span class="hljs-comment">//初始化线程信号量</span>

    <span class="hljs-keyword">if</span> (ret != <span class="hljs-number">0</span>)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Init sem failed.\n"</span>);  
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;  
}  

<span class="hljs-keyword">bool</span> CRedisPubClient::uninit()  
{  
    loop = NULL;  

    sem_destroy(&_event_sem);  

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;  
}  

<span class="hljs-keyword">bool</span> CRedisPubClient::connect()  
{    
    _redis_context = redisAsyncConnect(<span class="hljs-string">"127.0.0.1"</span>, <span class="hljs-number">6379</span>);    <span class="hljs-comment">// 异步连接到redis服务器上,使用6380端口</span>

    <span class="hljs-keyword">if</span> (NULL == _redis_context)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">": Connect redis failed.\n"</span>);  
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    <span class="hljs-keyword">if</span> (_redis_context->err)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Connect redis error: %d, %s\n"</span>,   
            _redis_context->err, _redis_context->errstr);    <span class="hljs-comment">// 输出错误信息  </span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    redisAeAttach(loop,_redis_context);   <span class="hljs-comment">// 将事件绑定到redis context上,使redis的回调跟事件关联  </span>

    <span class="hljs-comment">// 创建事件处理线程  </span>
    <span class="hljs-keyword">int</span> ret = pthread_create(&_event_thread, <span class="hljs-number">0</span>, &CRedisSubscriber::event_thread, <span class="hljs-keyword">this</span>);  

    <span class="hljs-keyword">if</span> (ret != <span class="hljs-number">0</span>)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Create event thread failed.\n"</span>);  
        disconnect();  
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    <span class="hljs-comment">// 设置连接回调,当异步调用连接后,服务器处理连接请求结束后调用,通知调用者连接的状态  </span>
    redisAsyncSetConnectCallback(_redis_context,   
        &CRedisSubClient::connect_callback);  

    <span class="hljs-comment">// 设置断开连接回调,当服务器断开连接后,通知调用者连接断开,调用者可以利用这个函数实现重连  </span>
    redisAsyncSetDisconnectCallback(_redis_context,  
        &CRedisSubClient::disconnect_callback);  

    <span class="hljs-comment">// 启动事件线程  </span>
    sem_post(&_event_sem);  
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;  
}  

<span class="hljs-keyword">bool</span> CRedisPubClient::disconnect()  
{  
    <span class="hljs-keyword">if</span> (_redis_context)  
    {  
        redisAsyncDisconnect(_redis_context);  
        redisAsyncFree(_redis_context);  
        _redis_context = NULL;  
    }  

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;  
}  

<span class="hljs-keyword">bool</span> CRedisPubClient::publish(<span class="hljs-keyword">const</span> <span class="hljs-built_in">string</span> &channel_name, <span class="hljs-keyword">const</span> <span class="hljs-built_in">string</span> &message)  
{  
    <span class="hljs-keyword">int</span> ret = redisAsyncCommand(_redis_context,   
        &CRedisPublisher::command_callback, <span class="hljs-keyword">this</span>, <span class="hljs-string">"PUBLISH %s %s"</span>,   
        channel_name.c_str(), message.c_str());  <span class="hljs-comment">//发布消息  </span>

    <span class="hljs-keyword">if</span> (REDIS_ERR == ret)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Publish command failed: %d\n"</span>, ret);  
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;  
    }  

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;    
}  

<span class="hljs-keyword">void</span> CRedisPubClient::connect_callback(<span class="hljs-keyword">const</span> redisAsyncContext *redis_context,  
    <span class="hljs-keyword">int</span> status)  
{  
    <span class="hljs-keyword">if</span> (status != REDIS_OK)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Error: %s\n"</span>, redis_context->errstr);  
    }  
    <span class="hljs-keyword">else</span>  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Redis connected!"</span>);  
    }  
}  

<span class="hljs-keyword">void</span> CRedisPubClient::disconnect_callback(  
    <span class="hljs-keyword">const</span> redisAsyncContext *redis_context, <span class="hljs-keyword">int</span> status)  
{  
    <span class="hljs-keyword">if</span> (status != REDIS_OK)  
    {    
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">": Error: %s\n"</span>, redis_context->errstr);  
    }  
}  

<span class="hljs-comment">// 消息接收回调函数  </span>
<span class="hljs-keyword">void</span> CRedisPubClient::command_callback(redisAsyncContext *redis_context,  
    <span class="hljs-keyword">void</span> *reply, <span class="hljs-keyword">void</span> *privdata)  
{  
    <span class="hljs-keyword">if</span> (NULL == reply || NULL == privdata) 
    {  
        <span class="hljs-keyword">return</span> ;  
    }  

    redisReply *redis_reply = <span class="hljs-keyword">reinterpret_cast</span><redisReply *>(reply); 

    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Publish: %s"</span>,redis_reply.str);
}  

<span class="hljs-keyword">void</span> *CRedisPubClient::event_thread(<span class="hljs-keyword">void</span> *data)  
{  
    <span class="hljs-keyword">if</span> (NULL == data)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">": Error!\n"</span>);  
        assert(<span class="hljs-keyword">false</span>);  
        <span class="hljs-keyword">return</span> NULL;  
    }  

    CRedisPubClient *self_this = <span class="hljs-keyword">reinterpret_cast</span><CRedisPubClient *>(data);  
    <span class="hljs-keyword">return</span> self_this->event_proc();  
}  

<span class="hljs-keyword">void</span> *CRedisPubClient::event_proc()  
{  
    sem_wait(&_event_sem);  

    <span class="hljs-comment">//进行事件处理循环  </span>
    aeMain(loop);  

    <span class="hljs-keyword">return</span> NULL;  
}  </code>
로그인 후 복사

测试封装的sub_client和pub_client类:

<code class=" hljs cpp"><span class="hljs-comment">//test_subpub.cpp</span>

<span class="hljs-preprocessor">#include "pub_client.h"</span>
<span class="hljs-preprocessor">#include "sub_client.h"  </span>

<span class="hljs-keyword">int</span> main(<span class="hljs-keyword">int</span> argc, <span class="hljs-keyword">char</span> *argv[])  
{  
    CRedisPubClient publisher;  
    CRedisSubClient subcriber;

    <span class="hljs-keyword">bool</span> ret_pub = publisher.init();  
    <span class="hljs-keyword">bool</span> ret_sub = subcriber.init();

    <span class="hljs-keyword">if</span> (!ret_sub&&!ret_pub)   
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Init failed.\n"</span>);  
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;  
    }  

    ret_pub = publisher.connect(); 
    ret_sub = subcriber.connect(); 

    <span class="hljs-keyword">if</span> (!ret_sub&&!ret_pub)  
    {  
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"connect failed."</span>);  
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;  
    }  

    subscriber.subcribe(<span class="hljs-string">"sports"</span>);

    <span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>)  
    {  
        publisher.publish(<span class="hljs-string">"sports"</span>, <span class="hljs-string">"ball"</span>);  
        sleep(<span class="hljs-number">1</span>);  
    }  

    publisher.disconnect();  
    publisher.uninit();  
    subscriber.disconnect();
    subscriber.disconnect();
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;  

} </code>
로그인 후 복사

终于到了编译链接运行的阶段,我在这个地方卡了快一天,期间各种编译、链接的错误。直接编译链接会出现找到不aeCreateFileEvent,aeDeleteFileEvent,aeMain等等错误。

  1. 先将redis/src文件夹中的ae.c,ae.h,ae_epoll.c,config.h,zmalloc.c,zmalloc.h拷贝至hiredis目录下
  2. 用gcc -c ae.c gcc -c zmalloc.c生成ae.o和zmalloc.o,利用这两个文件生成静态库 ar -r libar.a ae.o zmalloc.o
  3. 然后编译g++ -o test_subpub test_subpub.cpp pub_client.cpp sub_client.cpp -pthread -I ../ -I./ -I ../adapters ../libhiredis.a ../libae.a
  4. 运行./test_subpub

可以看到,客户端中消息在不断地滚动,即同时实现了订阅频道和发送消息的功能。

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

CUDA의 보편적인 행렬 곱셈: 입문부터 숙련까지! CUDA의 보편적인 행렬 곱셈: 입문부터 숙련까지! Mar 25, 2024 pm 12:30 PM

GEMM(일반 행렬 곱셈)은 많은 응용 프로그램과 알고리즘의 중요한 부분이며 컴퓨터 하드웨어 성능을 평가하는 중요한 지표 중 하나이기도 합니다. GEMM 구현에 대한 심층적인 연구와 최적화는 고성능 컴퓨팅과 소프트웨어와 하드웨어 시스템 간의 관계를 더 잘 이해하는 데 도움이 될 수 있습니다. 컴퓨터 과학에서 GEMM의 효과적인 최적화는 컴퓨팅 속도를 높이고 리소스를 절약할 수 있으며, 이는 컴퓨터 시스템의 전반적인 성능을 향상시키는 데 중요합니다. GEMM의 작동 원리와 최적화 방법에 대한 심층적인 이해는 현대 컴퓨팅 하드웨어의 잠재력을 더 잘 활용하고 다양하고 복잡한 컴퓨팅 작업에 대한 보다 효율적인 솔루션을 제공하는 데 도움이 될 것입니다. GEMM의 성능을 최적화하여

화웨이의 Qiankun ADS3.0 지능형 운전 시스템은 8월에 출시될 예정이며 처음으로 Xiangjie S9에 출시될 예정입니다. 화웨이의 Qiankun ADS3.0 지능형 운전 시스템은 8월에 출시될 예정이며 처음으로 Xiangjie S9에 출시될 예정입니다. Jul 30, 2024 pm 02:17 PM

7월 29일, AITO Wenjie의 400,000번째 신차 출시 행사에 Huawei 전무이사이자 Terminal BG 회장이자 Smart Car Solutions BU 회장인 Yu Chengdong이 참석하여 연설을 했으며 Wenjie 시리즈 모델이 출시될 것이라고 발표했습니다. 올해 출시 예정 지난 8월 Huawei Qiankun ADS 3.0 버전이 출시되었으며, 8월부터 9월까지 순차적으로 업그레이드를 추진할 계획입니다. 8월 6일 출시되는 Xiangjie S9에는 화웨이의 ADS3.0 지능형 운전 시스템이 최초로 탑재됩니다. LiDAR의 도움으로 Huawei Qiankun ADS3.0 버전은 지능형 주행 기능을 크게 향상시키고, 엔드투엔드 통합 기능을 갖추고, GOD(일반 장애물 식별)/PDP(예측)의 새로운 엔드투엔드 아키텍처를 채택합니다. 의사결정 및 제어), 주차공간부터 주차공간까지 스마트 드라이빙의 NCA 기능 제공, CAS3.0 업그레이드

Huawei 휴대폰에서 이중 WeChat 로그인을 구현하는 방법은 무엇입니까? Huawei 휴대폰에서 이중 WeChat 로그인을 구현하는 방법은 무엇입니까? Mar 24, 2024 am 11:27 AM

Huawei 휴대폰에서 이중 WeChat 로그인을 구현하는 방법은 무엇입니까? 소셜 미디어의 등장으로 WeChat은 사람들의 일상 생활에 없어서는 안될 커뮤니케이션 도구 중 하나가 되었습니다. 그러나 많은 사람들이 동일한 휴대폰에서 동시에 여러 WeChat 계정에 로그인하는 문제에 직면할 수 있습니다. Huawei 휴대폰 사용자의 경우 듀얼 WeChat 로그인을 달성하는 것은 어렵지 않습니다. 이 기사에서는 Huawei 휴대폰에서 듀얼 WeChat 로그인을 달성하는 방법을 소개합니다. 우선, 화웨이 휴대폰과 함께 제공되는 EMUI 시스템은 듀얼 애플리케이션 열기라는 매우 편리한 기능을 제공합니다. 앱 듀얼 오픈 기능을 통해 사용자는 동시에

WeChat에 표시되지 않는 경우 채팅을 복원하는 방법은 무엇입니까? WeChat은 채팅 복구 방법을 표시하지 않습니다 WeChat에 표시되지 않는 경우 채팅을 복원하는 방법은 무엇입니까? WeChat은 채팅 복구 방법을 표시하지 않습니다 Mar 13, 2024 pm 01:50 PM

요즘 가장 인기 있는 소셜 소프트웨어 중 하나인 WeChat은 풍부한 채팅 기능을 제공합니다. 그러나 때로는 "이 채팅을 표시하지 않음" 상황이 발생하여 중요한 대화가 숨겨지는 경우가 있습니다. 이러한 채팅을 복원하는 방법은 실제로 매우 간단합니다. 이 단계를 따르면 숨겨진 채팅을 쉽게 복원하고 WeChat이 제공하는 편리한 커뮤니케이션 경험을 계속 즐길 수 있습니다. WeChat에 표시되지 않는 경우 채팅을 복원하는 방법은 무엇입니까? 위챗은 채팅 복구 방법을 표시하지 않습니다. 방법 1: 위챗 메시지 목록에서 채팅 파트너의 이름이나 키워드를 직접 검색해보세요. 검색 결과가 발견되면 클릭하여 채팅 인터페이스로 들어가 채팅을 복원하고 표시할 수 있습니다. 방법 2, 친구 채팅을 통해 복원: 위챗을 열고 주소록을 클릭한 후 숨겨진 채팅에 표시된 친구를 찾아 클릭하여 메시지 보내기

PHP 프로그래밍 가이드: 피보나치 수열을 구현하는 방법 PHP 프로그래밍 가이드: 피보나치 수열을 구현하는 방법 Mar 20, 2024 pm 04:54 PM

프로그래밍 언어 PHP는 다양한 프로그래밍 논리와 알고리즘을 지원할 수 있는 강력한 웹 개발 도구입니다. 그중 피보나치 수열을 구현하는 것은 일반적이고 고전적인 프로그래밍 문제입니다. 이 기사에서는 PHP 프로그래밍 언어를 사용하여 피보나치 수열을 구현하는 방법을 소개하고 구체적인 코드 예제를 첨부합니다. 피보나치 수열은 다음과 같이 정의되는 수학적 수열입니다. 수열의 첫 번째와 두 번째 요소는 1이고 세 번째 요소부터 시작하여 각 요소의 값은 이전 두 요소의 합과 같습니다. 시퀀스의 처음 몇 가지 요소

항상 새로운! Huawei Mate60 시리즈가 HarmonyOS 4.2로 업그레이드: AI 클라우드 향상, Xiaoyi Dialect는 사용하기 매우 쉽습니다. 항상 새로운! Huawei Mate60 시리즈가 HarmonyOS 4.2로 업그레이드: AI 클라우드 향상, Xiaoyi Dialect는 사용하기 매우 쉽습니다. Jun 02, 2024 pm 02:58 PM

4월 11일, 화웨이는 처음으로 HarmonyOS 4.2 100개 시스템 업그레이드 계획을 공식 발표했습니다. 이번에는 휴대폰, 태블릿, 시계, 헤드폰, 스마트 스크린 및 기타 장치를 포함하여 180개 이상의 장치가 업그레이드에 참여할 것입니다. 지난달 HarmonyOS4.2 100대 업그레이드 계획이 꾸준히 진행됨에 따라 Huawei Pocket2, Huawei MateX5 시리즈, nova12 시리즈, Huawei Pura 시리즈 등을 포함한 많은 인기 모델도 업그레이드 및 적응을 시작했습니다. 더 많은 Huawei 모델 사용자가 HarmonyOS가 제공하는 일반적이고 종종 새로운 경험을 즐길 수 있을 것입니다. 사용자 피드백에 따르면 HarmonyOS4.2를 업그레이드한 후 Huawei Mate60 시리즈 모델의 경험이 모든 측면에서 개선되었습니다. 특히 화웨이 M

Apple 16 시스템의 어떤 버전이 가장 좋나요? Apple 16 시스템의 어떤 버전이 가장 좋나요? Mar 08, 2024 pm 05:16 PM

Apple 16 시스템의 최고 버전은 iOS16.1.4입니다. iOS16 시스템의 최고 버전은 사람마다 다를 수 있으며 일상적인 사용 경험의 추가 및 개선도 많은 사용자로부터 호평을 받았습니다. Apple 16 시스템의 가장 좋은 버전은 무엇입니까? 답변: iOS16.1.4 iOS 16 시스템의 가장 좋은 버전은 사람마다 다를 수 있습니다. 공개 정보에 따르면 2022년에 출시된 iOS16은 매우 안정적이고 성능이 뛰어난 버전으로 평가되며, 사용자들은 전반적인 경험에 상당히 만족하고 있습니다. 또한, iOS16에서는 새로운 기능 추가와 일상 사용 경험 개선도 많은 사용자들에게 호평을 받고 있습니다. 특히 업데이트된 배터리 수명, 신호 성능 및 발열 제어 측면에서 사용자 피드백은 비교적 긍정적이었습니다. 그러나 iPhone14를 고려하면

Huawei 휴대폰에서 WeChat 복제 기능을 구현하는 방법 Huawei 휴대폰에서 WeChat 복제 기능을 구현하는 방법 Mar 24, 2024 pm 06:03 PM

Huawei 휴대폰에서 WeChat 복제 기능을 구현하는 방법 소셜 소프트웨어의 인기와 개인 정보 보호 및 보안에 대한 사람들의 강조가 높아지면서 WeChat 복제 기능이 점차 주목을 받고 있습니다. WeChat 복제 기능을 사용하면 사용자가 동일한 휴대폰에서 여러 WeChat 계정에 동시에 로그인할 수 있으므로 관리 및 사용이 더 쉬워집니다. Huawei 휴대폰에서 WeChat 복제 기능을 구현하는 것은 어렵지 않습니다. 다음 단계만 따르면 됩니다. 1단계: 휴대폰 시스템 버전과 WeChat 버전이 요구 사항을 충족하는지 확인하십시오. 먼저 Huawei 휴대폰 시스템 버전과 WeChat 앱이 최신 버전으로 업데이트되었는지 확인하세요.

See all articles