日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

muduo网络库学习(二)对套接字和监听事件的封装Channel

發(fā)布時間:2024/4/19 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 muduo网络库学习(二)对套接字和监听事件的封装Channel 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

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)中的流程大致如下

  • Acceptor接收到客戶端請求,調(diào)用TcpServer回調(diào)函數(shù)
  • TcpServer回調(diào)函數(shù)中創(chuàng)建TcpConnection對象,代表著一個Tcp連接
  • TcpConnection構(gòu)造函數(shù)中創(chuàng)建Channel對象,保存客戶端套接字fd和關(guān)心的事件(可讀)
  • Channel注冊自己到所屬事件驅(qū)動循環(huán)(EventLoop)中的Poller上
  • Poller開始監(jiān)聽,當(dāng)發(fā)現(xiàn)Channel被激活后將其添加到EventLoop的激活隊列中
  • EventLoop在poll返回后處理激活隊列中的Channel,調(diào)用其處理函數(shù)
  • Channel在處理函數(shù)中根據(jù)被激活的原因調(diào)用不同的回調(diào)函數(shù)(可讀/可寫等)
  • 其中

  • Acceptor,監(jiān)聽類,用于監(jiān)聽客戶端請求,然后接收客戶端
  • TcpServer,服務(wù)器類,用于管理所有的TcpConnection
  • TcpConnection,Tcp連接類,代表一個Tcp連接,內(nèi)部保存對應(yīng)的Channel
  • Channel,套接字和相應(yīng)事件及回調(diào)函數(shù)的封裝,一個事件類
  • Poller,io復(fù)用的封裝,用于監(jiān)聽Channel
  • EventLoop,事件驅(qū)動主循環(huán),調(diào)用poll函數(shù),管理激活隊列,類似libevent的struct event_base

  • 函數(shù)對象std::function
    C++基于對象其實就是利用std::function/std::bind實現(xiàn)的,作用是綁定某個類的函數(shù)指針
    使用方法如

    #include <functional> typedef std::function<void()> Callback;void function_bind_test(Callback func) {func();/* 這里調(diào)用相當(dāng)于conn->connectionEstablished() */ }function_bind_test(std::bind(&TcpConnection::connectionEstablished, conn));

    std::function/std::bind為類的成員函數(shù)的調(diào)用提供更多靈活性,在C++11引入lambda后逐漸取代了std::bind,C++14后lambda支持模板參數(shù)后完全取代了std::bind


    Channel定義如下,成員函數(shù)主要就是

  • 設(shè)置回調(diào)函數(shù)set*Callback
  • 設(shè)置對fd關(guān)心的事件,將自己存儲的fd及相應(yīng)的事件注冊到Poller中enable*
  • 刪除對fd的監(jiān)聽,將其從Poller的ChannelMap中移除disableAll
  • 被激活時調(diào)用的回調(diào)函數(shù)hanleEvent
  • /* * Channel其實就是一個事件類,保存fd和需要監(jiān)聽的事件,以及各種回調(diào)函數(shù)* 類似libevent的struct event*/ class Channel : noncopyable {public:/* * function,可調(diào)用函數(shù)對象,類似函數(shù)指針* 模板參數(shù)是函數(shù)標(biāo)簽,例如* function<int(int, int)> func;* 這個函數(shù)對象的兩個參數(shù)類型都是int,返回值類型是int*/typedef std::function<void()> EventCallback;typedef std::function<void(Timestamp)> ReadEventCallback;Channel(EventLoop* loop, int fd);~Channel();/* 被激活后調(diào)用的函數(shù),內(nèi)部調(diào)用下面設(shè)置的幾個函數(shù) */void handleEvent(Timestamp receiveTime);/* 設(shè)置回調(diào)函數(shù) */void setReadCallback(ReadEventCallback cb){ readCallback_ = std::move(cb); }void setWriteCallback(EventCallback cb){ writeCallback_ = std::move(cb); }void setCloseCallback(EventCallback cb){ closeCallback_ = std::move(cb); }void setErrorCallback(EventCallback cb){ errorCallback_ = std::move(cb); }/// Tie this channel to the owner object managed by shared_ptr,/// prevent the owner object being destroyed in handleEvent./* 用于保存TcpConnection指針 */void tie(const std::shared_ptr<void>&);int fd() const { return fd_; }int events() const { return events_; }void set_revents(int revt) { revents_ = revt; } // used by pollers// int revents() const { return revents_; }bool isNoneEvent() const { return events_ == kNoneEvent; }/* 將自己注冊到Poller中,或從Poller中移除,或只刪除某個事件,update實現(xiàn) */void enableReading() { events_ |= kReadEvent; update(); }void disableReading() { events_ &= ~kReadEvent; update(); }void enableWriting() { events_ |= kWriteEvent; update(); }void disableWriting() { events_ &= ~kWriteEvent; update(); }void disableAll() { events_ = kNoneEvent; update(); }bool isWriting() const { return events_ & kWriteEvent; }bool isReading() const { return events_ & kReadEvent; }// for Pollerint index() { return index_; }void set_index(int idx) { index_ = idx; }// for debugstring reventsToString() const;string eventsToString() const;void doNotLogHup() { logHup_ = false; }EventLoop* ownerLoop() { return loop_; }void remove();private:static string eventsToString(int fd, int ev);void update();void handleEventWithGuard(Timestamp receiveTime);static const int kNoneEvent;static const int kReadEvent;static const int kWriteEvent;EventLoop* loop_;const int fd_;int events_;int revents_; // it's the received event types of epoll or poll/* * 保存fd在epoll/poll中的狀態(tài),有:* 還沒有添加到epoll中* 已經(jīng)添加到epoll中* 添加到epoll中,又從epoll中刪除了*/int index_; // used by Poller.bool logHup_;/* * tie_存儲的是TcpConnection類型的指針,即TcpConnectionPtr* 一個TcpConnection代表一個已經(jīng)建立好的Tcp連接*/std::weak_ptr<void> tie_;bool tied_;bool eventHandling_;bool addedToLoop_;ReadEventCallback readCallback_;EventCallback writeCallback_;EventCallback closeCallback_;EventCallback errorCallback_; };

    .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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。