ACE中的reactor模式學習

來源:互聯網
上載者:User

這兩天在學習C++網路編程卷二,第二章開始講到reactor模式,這兩天結合書上的例子,寫了個簡單的echo server端程式,代碼如下:

#include <ace/ACE.h>#include <ace/OS_NS_string.h>#include <ace/os_include/os_stdint.h>#include <ace/OS_NS_stdlib.h>#include <ace/OS_NS_errno.h>#include <ace/OS_NS_unistd.h>#include <ace/OS_NS_sys_time.h>#include <ace/OS_main.h>#include <ace/Basic_Types.h>#include <ace/Handle_Set.h>#include <ace/Map_Manager.h>#include <ace/Null_Mutex.h>#include <ace/Process.h>#include <ace/Thread_Manager.h>#include <ace/TSS_T.h>#include <ace/INET_Addr.h>#include <ace/SOCK_Acceptor.h>#include <ace/SOCK_Connector.h>#include <ace/SOCK_Stream.h>#include <ace/FILE_Connector.h>#include <ace/FILE_Addr.h>#include <ace/FILE_IO.h>#include <ace/Event_Handler.h>#include <ace/Reactor.h>//#include <stdarg.h>#include <assert.h>const u_short LISTEN_PORT = 8001;class EchoEventHandler : public ACE_Event_Handler{public:EchoEventHandler(ACE_Reactor* reactor, ACE_Time_Value timeout_time): ACE_Event_Handler(reactor), timeout_time_(timeout_time) , timer_id_(0){}virtual ~EchoEventHandler() {}int close(){return reactor()->remove_handler(this, ACE_Event_Handler::READ_MASK);}virtual int open(){// 測試退出的流程// return -1;// 註冊自己為反應器的讀處理事件if (0 != reactor()->register_handler(this, ACE_Event_Handler::READ_MASK)){ACE_ERROR_RETURN((LM_ERROR, "register read event fail\n"), -1);}// 同步目前時間last_proc_time_ = ACE_OS::gettimeofday();// 註冊定時器ACE_Time_Value reschedule(timeout_time_.sec() / 4);timer_id_ = reactor()->schedule_timer(this,   // timer event handler, should implement virtual function handle_timeout0,  // handle_timeout() callback argstimeout_time_, // delay timereschedule); // schedule intervalif (timer_id_ < 0){ACE_ERROR_RETURN((LM_ERROR, "register timer schedule fail, errno=%d\n", ACE_ERRNO_GET), -1);}// 測試退出的流程// return -1;return 0;}virtual int handle_input(ACE_HANDLE handle = ACE_INVALID_HANDLE){ACE_DEBUG((LM_ERROR, "get data from stream[%d]\n", peer().get_handle()));// 更新proc timelast_proc_time_ = ACE_OS::gettimeofday();// 通過stream收發資料char recvbuf[64];ACE_OS::memset(recvbuf, 0, sizeof(recvbuf));peer().enable(ACE_NONBLOCK);ssize_t recvlen = peer().recv(recvbuf, sizeof(recvbuf) - 1);if (recvlen < 0){ACE_ERROR_RETURN((LM_ERROR, "recv buffer fail, errno=%d \n", errno), -1);}else if (0 == recvlen){ACE_ERROR_RETURN((LM_ERROR, "client disconnect\n"), -1);}ACE_DEBUG((LM_ERROR, "recv buffer(%d):%s\n", recvlen,  recvbuf));ssize_t sendlen = peer().send_n(recvbuf, recvlen);if (sendlen != recvlen){ACE_ERROR_RETURN((LM_ERROR, "send back buffer fail \n"), -1);}return 0;}// 當handle_*方法返回-1時,會自動調用handle_close方法virtual int handle_close(ACE_HANDLE handle = ACE_INVALID_HANDLE,ACE_Reactor_Mask mask = 0){ACE_DEBUG((LM_ERROR, "event handle [fd=%d] close\n", peer_.get_handle()));// 盡量使用ID取消ACE_Event_Handler定時器, by sailzengif (timer_id_){reactor()->cancel_timer(timer_id_);}else{reactor()->cancel_timer(this);}peer_.close();delete this;return 0;}// 逾時處理virtual int handle_timeout(const ACE_Time_Value& current_time,const void* act = 0){if (current_time - last_proc_time_ >= timeout_time_){ACE_DEBUG((LM_ERROR, "timeout, peer fd=%d\n", peer().get_handle()));// remove handler 會去調用handle_close(), 與返回-1效果一樣reactor()->remove_handler(this, ACE_Event_Handler::READ_MASK);}return 0;}virtual ACE_HANDLE get_handle() const{return peer_.get_handle();}ACE_SOCK_Stream& peer(){return peer_;}private:ACE_SOCK_Stream peer_;ACE_Time_Value last_proc_time_;ACE_Time_Value timeout_time_;long timer_id_;};// event_handler 包含一個ACE_Reactorclass EchoAcceptor : public ACE_Event_Handler{public:EchoAcceptor(ACE_Reactor* reactor = ACE_Reactor::instance(),ACE_Time_Value conn_timeout = ACE_Time_Value(60)): ACE_Event_Handler(reactor),conn_timeout_(conn_timeout) {}virtual ~EchoAcceptor(){}virtual int open(const ACE_INET_Addr& local){if (0 != acceptor_.open(local, 1)){ACE_ERROR_RETURN((LM_ERROR, "acceptor open fail"), -1);}// 註冊自己為反應器的accpet處理事件reactor()->register_handler(this, ACE_Event_Handler::ACCEPT_MASK);ACE_DEBUG((LM_ERROR, "server[%s:%d] listening...\n", local.get_host_addr(), local.get_port_number()));return 0;}// 相應accept事件,作為一個工廠,產生EchoEventHandler對象virtual int handle_input(ACE_HANDLE handle = ACE_INVALID_HANDLE){EchoEventHandler* echo_handle = 0;ACE_NEW_RETURN(echo_handle, EchoEventHandler(reactor(), conn_timeout_), -1);if (-1 == acceptor_.accept(echo_handle->peer())){ACE_DEBUG((LM_ERROR, "accept fail\n"));delete echo_handle;return -1;}if (0 != echo_handle->open()){ACE_DEBUG((LM_ERROR, "echo handler open fail\n"));echo_handle->close();return -1;}return 0;}virtual int handle_close(ACE_HANDLE handle = ACE_INVALID_HANDLE,ACE_Reactor_Mask close_mask = 0){acceptor_.close();ACE_DEBUG((LM_ERROR, "accptor handle close, end reactor\n"));reactor()->end_reactor_event_loop();delete this;return 0;}virtual ACE_HANDLE get_handle() const{return acceptor_.get_handle();}// ACE_SOCK_Stream工廠ACE_SOCK_Acceptor acceptor_;private:ACE_Time_Value conn_timeout_;};int ACE_TMAIN(int argc, ACE_TCHAR* argv[]){EchoAcceptor* echosvr;ACE_Reactor reactor;ACE_NEW_RETURN(echosvr, EchoAcceptor(&reactor), -1);ACE_INET_Addr svraddr(LISTEN_PORT);echosvr->open(svraddr);if (0 != reactor.run_reactor_event_loop()){ACE_DEBUG((LM_ERROR, "run reactor event loop fail.\n"));return -1;}ACE_DEBUG((LM_ERROR, "event loop end.\n"));return 0;}

對比書上給的log的例子,有幾點說明一下:

1. 在ACE_Event_Handler的實現中,如果handle_*函數返回了-1,或者remove_handler時,會主動去調用handle_close函數。(ACE_Event_Handler屏蔽了內部的實現,剛開始接觸有點不太容易理解)

2. 書上的例子,在handle_close函數中,做了delete this來析構自己,讓我覺得很費解。(按照書上的說法,靜態分配的池子在close時由分配者自己去處理;如果是動態分配的話,在handle_close函數中處理比較優雅)。

後來發現:如果不再這裡析構的話,如果用戶端中斷連線時,我沒有更好的地方做析構,除了此處。。如果是寫成預分配串連池的話,也應該是在handle_close裡面實現自己的資源處理;


在windows下調試時碰到一處崩潰的bug:

是某次調試過程中,remove_handler之後再做reactor的析構時,程式會崩潰;

查看堆棧,發現在reactor.run_reactor_event_loop中會調用已經析構的EchoEventHandler導致的,對此表示很奇怪,明明已經remove handler並且析構了,為什麼reactor中還會有這個指標的(當然是無效指標)。


後來仔細看了一下書,發現了這兩句話:

……

3. Ensure that the lifetime of a registered event handler is longer than the reactor it's registered with if it can't be unregistered for some reason.

4. Avoid the use of the ACE_WFMO_Reactor since it defers the removal of event handlers, thereby making it hard toenforce convention 3。

終於清楚了,windows下預設的reactor實現是ACE_WFMO_Reactor, 實際上析構EchoEventHandler調用remove_handler時,內部實現延遲了真正的remove動作,但是自己已經被析構了,導致後面reactor真正做remove動作時記憶體不合法直接崩潰了;後來換成ACE_Select_Reactor試了一下,發現就ok了。

在寫測試程式的過程中,參考了《C++網路編程卷2》,以及ACE牛人sailzeng的一篇文章:

http://blog.csdn.net/fullsail/article/details/2915685


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.