muduo网络库学习(二)对套接字和监听事件的封装Channel
muduo對描述符fd,需要監(jiān)聽的事件events,當(dāng)fd被激活調(diào)用的可讀/可寫/關(guān)閉/錯誤回調(diào)函數(shù)進(jìn)行了封裝,實現(xiàn)在Channel類中,Poller監(jiān)聽的其實就是一個個Channel對象,Channel可以向Poller注冊自己關(guān)心的事件,當(dāng)被激活后調(diào)用相應(yīng)的回調(diào)函數(shù)。類似libevent的struct event。
Channel在整個事件驅(qū)動循環(huán)中的流程大致如下
其中
函數(shù)對象std::function
C++基于對象其實就是利用std::function/std::bind實現(xiàn)的,作用是綁定某個類的函數(shù)指針
使用方法如
std::function/std::bind為類的成員函數(shù)的調(diào)用提供更多靈活性,在C++11引入lambda后逐漸取代了std::bind,C++14后lambda支持模板參數(shù)后完全取代了std::bind
Channel定義如下,成員函數(shù)主要就是
.cpp中的幾個比較重要的函數(shù)介紹
/* * 由TcpConnection中connectEstablished函數(shù)調(diào)用* 作用是當(dāng)建立起一個Tcp連接后,讓用于監(jiān)測fd的Channel保存這個Tcp連接的弱引用* tie_是weak_ptr的原因* weak_ptr是弱引用,不增加引用基數(shù),當(dāng)需要調(diào)用內(nèi)部指針時* 可通過lock函數(shù)提升成一個shared_ptr,如果內(nèi)部的指針已經(jīng)銷毀* 那么提升的shared_ptr是null* 可以通過是否是null判斷Tcp是否還處于連接,因為如果斷開,那么這個TcpConnection就被銷毀了*/ void Channel::tie(const std::shared_ptr<void>& obj) {tie_ = obj;tied_ = true; }因為Channel的回調(diào)函數(shù)其實調(diào)用的是TcpConnection中的函數(shù),所以如果想要調(diào)用,就必須知道TcpConnection的指針,而如果TcpConnection被銷毀了,那么指針調(diào)用會出錯,所以采用智能指針,又因為Channel只需要知道TcpConnection是否還存在,所以不需要使用shared_ptr,這會增加引用計數(shù),導(dǎo)致TcpConnection銷毀不了(如果close連接,TcpConnection本應(yīng)該被銷毀,但是因為Channel中也留有對TcpConnection的引用,所以不會銷毀,糟糕的事情…),所以采用weak_ptr,不增加引用計數(shù),適當(dāng)時可以提升為shared_ptr
/** 根據(jù)fd激活事件的不同,調(diào)用不同的fd的回調(diào)函數(shù)*/ void Channel::handleEvent(Timestamp receiveTime) {/* * RAII,對象管理資源* weak_ptr使用lock提升成shared_ptr,此時引用計數(shù)加一* 函數(shù)返回,棧空間對象銷毀,提升的shared_ptr guard銷毀,引用計數(shù)減一*/std::shared_ptr<void> guard;if (tied_){guard = tie_.lock();if (guard){handleEventWithGuard(receiveTime);}}else{handleEventWithGuard(receiveTime);} }
經(jīng)典的RAII應(yīng)用場景,配合智能指針
如果想要判斷Channel所在的TcpConnection是否仍然存在(沒有被關(guān)閉),可以直接將weak_ptr提升為shared_ptr,如果提升成功(不為NULL),代表TcpConnection存在,調(diào)用回調(diào)函數(shù)即可。
提升成shared_ptr后TcpConnection引用計數(shù)變?yōu)?,handleEvent函數(shù)返回后局部變量guard銷毀,引用計數(shù)恢復(fù)到1。
同樣的應(yīng)用還有鎖對象,創(chuàng)建時上鎖,析構(gòu)時解鎖
handleEventWithGuard其實就是根據(jù)不同的激活原因滴啊用不同的回調(diào)函數(shù),這些回調(diào)函數(shù)都在TcpConnection中,也是通過上面std::function/std::bind設(shè)置的,所以在調(diào)用前必須判斷Channel所在的TcpConnection是否還存在
/** 根據(jù)被激活事件的不同,調(diào)用不同的回調(diào)函數(shù)*/ void Channel::handleEventWithGuard(Timestamp receiveTime) {eventHandling_ = true;LOG_TRACE << reventsToString();if ((revents_ & POLLHUP) && !(revents_ & POLLIN)){if (logHup_){LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";}if (closeCallback_) closeCallback_();}if (revents_ & POLLNVAL){LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL";}if (revents_ & (POLLERR | POLLNVAL)){if (errorCallback_) errorCallback_();}if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)){if (readCallback_) readCallback_(receiveTime);}if (revents_ & POLLOUT){if (writeCallback_) writeCallback_();}eventHandling_ = false; }總結(jié)
以上是生活随笔為你收集整理的muduo网络库学习(二)对套接字和监听事件的封装Channel的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: muduo网络库学习(一)对io复用的封
- 下一篇: muduo网络库学习(三)定时器Time