青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

loop_in_codes

低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

Muduo源碼閱讀

最近簡單讀了下muduo的源碼,本文對其主要實現/結構簡單總結下。

muduo的主要源碼位于net文件夾下,base文件夾是一些基礎代碼,不影響理解網絡部分的實現。muduo主要類包括:

  • EventLoop
  • Channel
  • Poller
  • TcpConnection
  • TcpClient
  • TcpServer
  • Connector
  • Acceptor
  • EventLoopThread
  • EventLoopThreadPool

其中,Poller(及其實現類)包裝了Poll/EPoll,封裝了OS針對設備(fd)的操作;Channel是設備fd的包裝,在muduo中主要包裝socket;TcpConnection抽象一個TCP連接,無論是客戶端還是服務器只要建立了網絡連接就會使用TcpConnection;TcpClient/TcpServer分別抽象TCP客戶端和服務器;Connector/Acceptor分別包裝TCP客戶端和服務器的建立連接/接受連接;EventLoop是一個主控類,是一個事件發生器,它驅動Poller產生/發現事件,然后將事件派發到Channel處理;EventLoopThread是一個帶有EventLoop的線程;EventLoopThreadPool自然是一個EventLoopThread的資源池,維護一堆EventLoopThread。

閱讀庫源碼時可以從庫的接口層著手,看看關鍵功能是如何實現的。對于muduo而言,可以從TcpServer/TcpClient/EventLoop/TcpConnection這幾個類著手。接下來看看主要功能的實現:

建立連接

    TcpClient::connect 
        -> Connector::start 
            -> EventLoop::runInLoop(Connector::startInLoop...
            -> Connector::connect             

EventLoop::runInLoop接口用于在this所在的線程運行某個函數,這個后面看下EventLoop的實現就可以了解。 網絡連接的最終建立是在Connector::connect中實現,建立連接之后會創建一個Channel來代表這個socket,并且綁定事件監聽接口。最后最重要的是,調用Channel::enableWritingChannel有一系列的enableXX接口,這些接口用于標識自己關心某IO事件。后面會看到他們的實現。

Connector監聽的主要事件無非就是連接已建立,用它監聽讀數據/寫數據事件也不符合設計。TcpConnection才是做這種事的。

客戶端收發數據

當Connector發現連接真正建立好后,會回調到TcpClient::newConnection,在TcpClient構造函數中:

    connector_->setNewConnectionCallback(
      boost::bind(&TcpClient::newConnection, this, _1));

TcpClient::newConnection中創建一個TcpConnection來代表這個連接:

    TcpConnectionPtr conn(new TcpConnection(loop_,
                                            connName,
                                            sockfd,
                                            localAddr,
                                            peerAddr));

    conn->setConnectionCallback(connectionCallback_);
    conn->setMessageCallback(messageCallback_);
    conn->setWriteCompleteCallback(writeCompleteCallback_);
    ...
    conn->connectEstablished();

并同時設置事件回調,以上設置的回調都是應用層(即庫的使用者)的接口。每一個TcpConnection都有一個Channel,畢竟每一個網絡連接都對應了一個socket fd。在TcpConnection構造函數中創建了一個Channel,并設置事件回調函數。

TcpConnection::connectEstablished函數最主要的是通知Channel自己開始關心IO讀取事件:

    void TcpConnection::connectEstablished()
    {
        ...
        channel_->enableReading();

這是自此我們看到的第二個Channel::enableXXX接口,這些接口是如何實現關心IO事件的呢?這個后面講到。

muduo的數據發送都是通過TcpConnection::send完成,這個就是一般網絡庫中在不使用OS的異步IO情況下的實現:緩存應用層傳遞過來的數據,在IO設備可寫的情況下盡量寫入數據。這個主要實現在TcpConnection::sendInLoop中。

    TcpConnection::sendInLoop(....) {
        ...
        // if no thing in output queue, try writing directly
        if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)  // 設備可寫且沒有緩存時立即寫入
        { 
            nwrote = sockets::write(channel_->fd(), data, len);
        }
        ...
        // 否則加入數據到緩存,等待IO可寫時再寫
        outputBuffer_.append(static_cast<const char*>(data)+nwrote, remaining);
        if (!channel_->isWriting())
        {
            // 注冊關心IO寫事件,Poller就會對寫做檢測
            channel_->enableWriting();
        }
        ...     
    }

當IO可寫時,Channel就會回調TcpConnection::handleWrite(構造函數中注冊)

    void TcpConnection::handleWrite()
    {
        ...
        if (channel_->isWriting())
        {
            ssize_t n = sockets::write(channel_->fd(),
                               outputBuffer_.peek(),
                               outputBuffer_.readableBytes());

服務器端的數據收發同客戶端機制一致,不同的是連接(TcpConnection)的建立方式不同。

服務器接收連接

服務器接收連接的實現在一個網絡庫中比較重要。muduo中通過Acceptor類來接收連接。在TcpClient中,其Connector通過一個關心Channel可寫的事件來通過連接已建立;在Acceptor中則是通過一個Channel可讀的事件來表示有新的連接到來:

    Acceptor::Acceptor(....) {
        ...
        acceptChannel_.setReadCallback(
            boost::bind(&Acceptor::handleRead, this));
        ... 
    }

    void Acceptor::handleRead()
    {
        ...
        int connfd = acceptSocket_.accept(&peerAddr); // 接收連接獲得一個新的socket
        if (connfd >= 0)
        {
            ...
            newConnectionCallback_(connfd, peerAddr); // 回調到TcpServer::newConnection

TcpServer::newConnection中建立一個TcpConnection,并將其附加到一個EventLoopThread中,簡單來說就是給其配置一個線程:

    void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
    {
        ...
        EventLoop* ioLoop = threadPool_->getNextLoop();
        TcpConnectionPtr conn(new TcpConnection(ioLoop,
                                                connName,
                                                sockfd,
                                                localAddr,
                                                peerAddr));
        connections_[connName] = conn;
        ...
        ioLoop->runInLoop(boost::bind(&TcpConnection::connectEstablished, conn));

IO的驅動

之前提到,一旦要關心某IO事件了,就調用Channel::enableXXX,這個如何實現的呢?

    class Channel {
        ...
        void enableReading() { events_ |= kReadEvent; update(); }
        void enableWriting() { events_ |= kWriteEvent; update(); }
       
    void Channel::update()
    {
        loop_->updateChannel(this);
    }

    void EventLoop::updateChannel(Channel* channel)
    {
        ...
        poller_->updateChannel(channel);
    }

最終調用到Poller::upateChannel。muduo中有兩個Poller的實現,分別是Poll和EPoll,可以選擇簡單的Poll來看:

    void PollPoller::updateChannel(Channel* channel)
    {
      ...
      if (channel->index() < 0)
      {
        // a new one, add to pollfds_
        assert(channels_.find(channel->fd()) == channels_.end());
        struct pollfd pfd;
        pfd.fd = channel->fd();
        pfd.events = static_cast<short>(channel->events()); // 也就是Channel::enableXXX操作的那個events_
        pfd.revents = 0;
        pollfds_.push_back(pfd); // 加入一個新的pollfd
        int idx = static_cast<int>(pollfds_.size())-1;
        channel->set_index(idx);
        channels_[pfd.fd] = channel;

可見Poller就是把Channel關心的IO事件轉換為OS提供的IO模型數據結構上。通過查看關鍵的pollfds_的使用,可以發現其主要是在Poller::poll接口里。這個接口會在EventLoop的主循環中不斷調用:

    void EventLoop::loop()
    {
      ...
      while (!quit_)
      {
        activeChannels_.clear();
        pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
        ...
        for (ChannelList::iterator it = activeChannels_.begin();
            it != activeChannels_.end(); ++it)
        {
          currentActiveChannel_ = *it;
          currentActiveChannel_->handleEvent(pollReturnTime_); // 獲得IO事件,通知各注冊回調
        }

整個流程可總結為:各Channel內部會把自己關心的事件告訴給Poller,Poller由EventLoop驅動檢測IO,然后返回哪些Channel發生了事件,EventLoop再驅動這些Channel調用各注冊回調。

從這個過程中可以看出,EventLoop就是一個事件產生器。

線程模型

在muduo的服務器中,muduo的線程模型是怎樣的呢?它如何通過線程來支撐高并發呢?其實很簡單,它為每一個線程配置了一個EventLoop,這個線程同時被附加了若干個網絡連接,這個EventLoop服務于這些網絡連接,為這些連接收集并派發IO事件。

回到TcpServer::newConnection中:

    void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
    {
      ...
      EventLoop* ioLoop = threadPool_->getNextLoop();
      ...
      TcpConnectionPtr conn(new TcpConnection(ioLoop, // 使用這個選擇到的線程中的EventLoop
                                              connName,
                                              sockfd,
                                              localAddr,
                                              peerAddr));
      ...
      ioLoop->runInLoop(boost::bind(&TcpConnection::connectEstablished, conn));

注意TcpConnection::connectEstablished是如何通過Channel注冊關心的IO事件到ioLoop的。

極端來說,muduo的每一個連接線程可以只為一個網絡連接服務,這就有點類似于thread per connection模型了。

網絡模型

傳說中的Reactor模式,以及one loop per thread,基于EventLoop的作用,以及線程池與TcpConnection的關系,可以醍醐灌頂般理解以下這張muduo的網絡模型圖了:


總結

本文主要對muduo的主要結構及主要機制的實現做了描述,其他如Buffer的實現、定時器的實現大家都可以自行研究。muduo的源碼很清晰,通過源碼及配合陳碩博客上的內容可以學到一些網絡編程方面的經驗。

posted on 2014-05-04 18:22 Kevin Lynx 閱讀(14611) 評論(3)  編輯 收藏 引用 所屬分類: c/c++ 、network

評論

# re: Muduo源碼閱讀[未登錄] 2014-05-04 21:43 春秋十二月

這個庫的代碼比較簡單  回復  更多評論   

# re: Muduo源碼閱讀 2014-05-05 10:47 Enic

樓主用的什么工具看的?。?nbsp; 回復  更多評論   

# re: Muduo源碼閱讀 2014-05-05 19:38 Kevin Lynx

@Enic
就vim,tag都沒用  回復  更多評論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美午夜在线观看| 欧美亚洲一级| 久久免费视频观看| 亚洲国产精品一区二区久| 榴莲视频成人在线观看| 欧美日韩1区2区3区| 久久久久久高潮国产精品视| 一区二区日韩免费看| 99视频精品| 国产一区二区精品久久91| 欧美一区成人| 亚洲砖区区免费| 99精品国产福利在线观看免费| 久久不射电影网| 欧美视频日韩视频在线观看| 亚洲欧美视频一区| 一本色道**综合亚洲精品蜜桃冫| 亚洲久久成人| 有码中文亚洲精品| 午夜伦欧美伦电影理论片| 亚洲影院免费观看| 亚洲欧美另类国产| 亚洲在线不卡| 久久日韩精品| 玖玖综合伊人| 久久精品中文字幕一区| 亚洲人在线视频| 欧美第一黄网免费网站| 亚洲电影自拍| 亚洲男同1069视频| 亚洲女爱视频在线| 国产欧美亚洲日本| 91久久久亚洲精品| 亚洲欧美综合国产精品一区| 久久久久久久一区二区三区| 久久深夜福利| 国产精品久久久久久久久久妞妞 | 久久久久久9999| 欧美一区2区三区4区公司二百| 久久国产夜色精品鲁鲁99| 亚洲每日更新| 久久久人成影片一区二区三区观看 | 欧美日韩激情小视频| 亚洲欧美韩国| 亚洲欧洲另类| 久久久7777| 欧美性猛交99久久久久99按摩 | 黄色国产精品一区二区三区| 亚洲精品裸体| 欧美日韩综合| 国产综合自拍| 一本色道久久综合狠狠躁篇的优点 | 亚洲影院免费观看| 欧美视频一区二区三区在线观看 | 老鸭窝亚洲一区二区三区| 亚洲精品综合在线| 国产精品a久久久久久| 亚洲欧美日本伦理| 亚洲视频一二| 国产日韩精品一区二区三区在线 | 欧美在线观看一二区| 久久久久成人网| 亚洲午夜精品一区二区| 亚洲福利小视频| 久久久久久久国产| 狠狠色狠狠色综合人人| 免费观看一区| 美女尤物久久精品| 久热精品在线视频| 国产精品99久久久久久人| 亚洲一区在线直播| 亚洲色图在线视频| 国产一区二区三区免费在线观看| 久久精品99国产精品酒店日本| 午夜精品久久久久久久白皮肤| 在线看一区二区| 一区二区三区在线观看欧美| 欧美成年视频| 欧美一区二区精品在线| 性欧美1819sex性高清| 欧美在线不卡| 欧美成人午夜激情在线| 欧美一区二区私人影院日本 | 一区二区三区国产精品| 亚洲在线免费视频| 亚洲激情在线激情| 亚洲精品视频在线观看免费| 麻豆亚洲精品| 久久精品卡一| 久久综合网络一区二区| 欧美日韩综合视频| 亚洲理论在线| 欧美在线啊v| 国产精品第一区| 欧美国产高潮xxxx1819| 黄色综合网站| 欧美日韩国产电影| 亚洲手机成人高清视频| 欧美精品日日鲁夜夜添| 暖暖成人免费视频| 国产精品久久久久久久免费软件| 亚洲日本欧美在线| 国产欧美亚洲精品| 激情五月婷婷综合| 正在播放欧美视频| 久久夜色精品国产亚洲aⅴ | 亚洲在线播放电影| 91久久精品国产91久久性色tv| 久久久国产一区二区三区| 欧美xx69| 亚洲国产日韩欧美| 美女国产精品| 欧美精品一区二区三| 久久久久中文| 亚洲黄网站在线观看| 久久精品亚洲| 亚洲精品资源| 免费看黄裸体一级大秀欧美| 在线观看精品一区| 欧美亚洲日本网站| 国内一区二区三区在线视频| 亚洲免费视频网站| 欧美一区二区在线| 麻豆成人综合网| 亚洲精品在线一区二区| 亚洲欧美视频一区| 亚洲毛片在线观看.| 国产欧美亚洲日本| 欧美黄色影院| 国产精品久久久久久久久婷婷 | 欧美**字幕| 久久疯狂做爰流白浆xx| 欧美极品影院| 久久综合久久综合九色| 欧美性天天影院| 欧美自拍偷拍| 欧美精品一区二区三区很污很色的| 老司机精品福利视频| 国产精品国产三级国产专区53| 99re热这里只有精品免费视频| 亚洲高清免费视频| 久久另类ts人妖一区二区| 欧美一区二区三区免费视| 国产精品揄拍一区二区| 亚洲三级视频| av不卡免费看| 亚洲综合清纯丝袜自拍| 欧美国产精品中文字幕| 在线播放日韩欧美| 欧美在线free| 欧美一区二区三区喷汁尤物| 欧美日本韩国一区二区三区| 欧美激情中文字幕一区二区| 国产亚洲欧美日韩日本| 一本色道久久综合亚洲精品婷婷| 亚洲精品日本| 欧美jizz19性欧美| 亚洲电影成人| 欧美在线啊v一区| 国产精品久久久久久久午夜片| 亚洲精品一区二区三区av| 亚洲激情在线观看视频免费| 久久亚洲一区二区| 免费一级欧美在线大片| 亚洲精品免费看| 亚洲校园激情| 国产精品美女www爽爽爽视频| 一本一本久久a久久精品综合妖精| 亚洲视频欧美在线| 亚洲午夜精品久久久久久app| 亚洲欧美综合国产精品一区| 欧美大片在线观看一区| 亚洲电影免费在线| 一本色道久久综合亚洲精品按摩| 欧美精品色综合| 亚洲欧美日韩国产综合精品二区| 欧美伦理在线观看| 国产精品美女久久久久久2018 | 亚洲一区二区三区精品动漫| 欧美喷潮久久久xxxxx| 欧美一区二区精品| 欧美激情2020午夜免费观看| 亚洲私人影吧| 国产一区二区在线观看免费播放| 久久久久久一区二区三区| 久久成人免费| 亚洲精品乱码久久久久久按摩观| 国产麻豆精品视频| 老司机午夜精品视频| 老色批av在线精品| 亚洲专区欧美专区| 亚洲少妇诱惑| 欧美在线999| 一区视频在线| 国产精品二区在线| 久久国产综合精品| 亚洲午夜91| 欧美制服丝袜| 亚洲欧美日韩精品综合在线观看| 亚洲精品国产精品乱码不99|