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

loop_in_codes

低調(diào)做技術(shù)__歡迎移步我的獨(dú)立博客 codemaro.com 微博 kevinlynx

Muduo源碼閱讀

最近簡單讀了下muduo的源碼,本文對其主要實(shí)現(xiàn)/結(jié)構(gòu)簡單總結(jié)下。

muduo的主要源碼位于net文件夾下,base文件夾是一些基礎(chǔ)代碼,不影響理解網(wǎng)絡(luò)部分的實(shí)現(xiàn)。muduo主要類包括:

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

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

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

建立連接

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

EventLoop::runInLoop接口用于在this所在的線程運(yùn)行某個函數(shù),這個后面看下EventLoop的實(shí)現(xiàn)就可以了解。 網(wǎng)絡(luò)連接的最終建立是在Connector::connect中實(shí)現(xiàn),建立連接之后會創(chuàng)建一個Channel來代表這個socket,并且綁定事件監(jiān)聽接口。最后最重要的是,調(diào)用Channel::enableWritingChannel有一系列的enableXX接口,這些接口用于標(biāo)識自己關(guān)心某IO事件。后面會看到他們的實(shí)現(xiàn)。

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

客戶端收發(fā)數(shù)據(jù)

當(dāng)Connector發(fā)現(xiàn)連接真正建立好后,會回調(diào)到TcpClient::newConnection,在TcpClient構(gòu)造函數(shù)中:

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

TcpClient::newConnection中創(chuàng)建一個TcpConnection來代表這個連接:

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

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

并同時設(shè)置事件回調(diào),以上設(shè)置的回調(diào)都是應(yīng)用層(即庫的使用者)的接口。每一個TcpConnection都有一個Channel,畢竟每一個網(wǎng)絡(luò)連接都對應(yīng)了一個socket fd。在TcpConnection構(gòu)造函數(shù)中創(chuàng)建了一個Channel,并設(shè)置事件回調(diào)函數(shù)。

TcpConnection::connectEstablished函數(shù)最主要的是通知Channel自己開始關(guān)心IO讀取事件:

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

這是自此我們看到的第二個Channel::enableXXX接口,這些接口是如何實(shí)現(xiàn)關(guān)心IO事件的呢?這個后面講到。

muduo的數(shù)據(jù)發(fā)送都是通過TcpConnection::send完成,這個就是一般網(wǎng)絡(luò)庫中在不使用OS的異步IO情況下的實(shí)現(xiàn):緩存應(yīng)用層傳遞過來的數(shù)據(jù),在IO設(shè)備可寫的情況下盡量寫入數(shù)據(jù)。這個主要實(shí)現(xiàn)在TcpConnection::sendInLoop中。

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

當(dāng)IO可寫時,Channel就會回調(diào)TcpConnection::handleWrite(構(gòu)造函數(shù)中注冊)

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

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

服務(wù)器接收連接

服務(wù)器接收連接的實(shí)現(xiàn)在一個網(wǎng)絡(luò)庫中比較重要。muduo中通過Acceptor類來接收連接。在TcpClient中,其Connector通過一個關(guān)心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); // 回調(diào)到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的驅(qū)動

之前提到,一旦要關(guān)心某IO事件了,就調(diào)用Channel::enableXXX,這個如何實(shí)現(xiàn)的呢?

    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);
    }

最終調(diào)用到Poller::upateChannel。muduo中有兩個Poller的實(shí)現(xiàn),分別是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關(guān)心的IO事件轉(zhuǎn)換為OS提供的IO模型數(shù)據(jù)結(jié)構(gòu)上。通過查看關(guān)鍵的pollfds_的使用,可以發(fā)現(xiàn)其主要是在Poller::poll接口里。這個接口會在EventLoop的主循環(huán)中不斷調(diào)用:

    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事件,通知各注冊回調(diào)
        }

整個流程可總結(jié)為:各Channel內(nèi)部會把自己關(guān)心的事件告訴給Poller,Poller由EventLoop驅(qū)動檢測IO,然后返回哪些Channel發(fā)生了事件,EventLoop再驅(qū)動這些Channel調(diào)用各注冊回調(diào)。

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

線程模型

在muduo的服務(wù)器中,muduo的線程模型是怎樣的呢?它如何通過線程來支撐高并發(fā)呢?其實(shí)很簡單,它為每一個線程配置了一個EventLoop,這個線程同時被附加了若干個網(wǎng)絡(luò)連接,這個EventLoop服務(wù)于這些網(wǎng)絡(luò)連接,為這些連接收集并派發(fā)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注冊關(guān)心的IO事件到ioLoop的。

極端來說,muduo的每一個連接線程可以只為一個網(wǎng)絡(luò)連接服務(wù),這就有點(diǎn)類似于thread per connection模型了。

網(wǎng)絡(luò)模型

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


總結(jié)

本文主要對muduo的主要結(jié)構(gòu)及主要機(jī)制的實(shí)現(xiàn)做了描述,其他如Buffer的實(shí)現(xiàn)、定時器的實(shí)現(xiàn)大家都可以自行研究。muduo的源碼很清晰,通過源碼及配合陳碩博客上的內(nèi)容可以學(xué)到一些網(wǎng)絡(luò)編程方面的經(jīng)驗(yàn)。

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

評論

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

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

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

樓主用的什么工具看的啊?  回復(fù)  更多評論   

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

@Enic
就vim,tag都沒用  回復(fù)  更多評論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲综合日韩中文字幕v在线| 午夜精品福利在线观看| 欧美韩日视频| 欧美精品一区在线播放| 欧美特黄一级| 国产欧美精品一区二区色综合| 国产精品国产三级国产专播品爱网 | 亚洲一级一区| 亚洲在线观看免费视频| 欧美亚洲尤物久久| 免费观看亚洲视频大全| 亚洲激情视频网站| 中日韩高清电影网| 久久免费黄色| 欧美日韩亚洲综合一区| 国产网站欧美日韩免费精品在线观看| 黄色精品在线看| 99v久久综合狠狠综合久久| 亚洲欧洲av一区二区| 美女视频黄a大片欧美| 亚洲乱码国产乱码精品精98午夜| 亚洲香蕉伊综合在人在线视看| 久久xxxx精品视频| 老鸭窝亚洲一区二区三区| 欧美激情按摩| 国产日韩欧美91| 亚洲日本欧美日韩高观看| 亚洲天堂网在线观看| 久久精品在线播放| 亚洲精品一区二区网址| 久久精品国产99国产精品澳门| 欧美日本高清一区| 尤妮丝一区二区裸体视频| 亚洲一区影音先锋| 欧美激情精品久久久六区热门 | 欧美一级视频| 亚洲国产精彩中文乱码av在线播放| 99在线精品视频| 鲁大师影院一区二区三区| 国产精品爽爽爽| 亚洲天堂免费观看| 亚洲第一天堂无码专区| 久久岛国电影| 国产亚洲第一区| 欧美一级二级三级蜜桃| 一本不卡影院| 欧美日本在线一区| 日韩午夜在线| 91久久精品网| 欧美jizzhd精品欧美巨大免费| 国产在线不卡| 久久男人av资源网站| 欧美亚洲在线视频| 国产日韩欧美在线观看| 欧美在线黄色| 香蕉成人久久| 国语自产精品视频在线看一大j8 | 中文网丁香综合网| 欧美另类一区二区三区| 亚洲精品小视频| 亚洲国产精品一区二区第一页| 久久久噜噜噜久久人人看| 合欧美一区二区三区| 久久欧美中文字幕| 欧美一区二区高清在线观看| 国产乱人伦精品一区二区| 欧美一级久久久久久久大片| 亚洲欧美电影在线观看| 国产日韩欧美在线一区| 久久亚洲国产精品日日av夜夜| 欧美中文字幕不卡| 136国产福利精品导航网址应用| 免费在线成人av| 欧美成ee人免费视频| 99热在这里有精品免费| 午夜精品亚洲一区二区三区嫩草| 亚洲欧美另类国产| 亚洲一区精品视频| 国产一区二区福利| 美女黄网久久| 欧美日韩高清在线播放| 欧美一区二区视频观看视频| 久久精品亚洲一区二区| 亚洲国产美女| 一区二区三区日韩在线观看| 国产日韩欧美亚洲| 欧美高清视频| 国产精品久久久久aaaa樱花| 六月丁香综合| 欧美极品在线播放| 欧美一区二区日韩| 久久亚洲综合网| 亚洲桃花岛网站| 久久久国际精品| 一本一本久久a久久精品牛牛影视| 亚洲一区免费在线观看| 在线免费观看成人网| 一区二区电影免费观看| 亚洲第一精品夜夜躁人人躁| 亚洲视频在线观看三级| 亚洲第一中文字幕| 亚洲一区在线直播| 亚洲国产日韩综合一区| 亚洲一区3d动漫同人无遮挡| 亚洲国产一区二区三区在线播| 亚洲网址在线| 亚洲精品视频在线观看免费| 性做久久久久久久久| 亚洲视频一区二区免费在线观看| 午夜久久久久久| 夜夜嗨av一区二区三区| 久久五月婷婷丁香社区| 欧美一区二区三区在| 欧美日韩一区高清| 亚洲国产精品久久人人爱蜜臀| 国产亚洲精品一区二555| 一区二区三区日韩| 亚洲一区国产一区| 欧美激情第五页| 欧美1区2区视频| 国产一区日韩二区欧美三区| 亚洲网站视频| 亚洲一区二区三区中文字幕| 欧美精品偷拍| 亚洲欧洲在线播放| 亚洲精品一二| 欧美极品一区| 亚洲免费精彩视频| aa国产精品| 欧美日韩成人综合在线一区二区| 欧美国产在线视频| 亚洲国产成人av| 另类图片综合电影| 欧美不卡在线| 91久久精品日日躁夜夜躁国产| 久久福利电影| 免费观看一级特黄欧美大片| 好看不卡的中文字幕| 久久精品国产久精国产思思| 久久人人97超碰人人澡爱香蕉| 国产主播在线一区| 亚洲男人的天堂在线| 欧美国产欧美综合| 国产精品福利久久久| 亚洲欧洲另类国产综合| 91久久在线| 欧美经典一区二区三区| 91久久综合| 亚洲特黄一级片| 国产精品一区二区欧美| 亚洲欧美在线另类| 久久国产欧美| 亚洲成人在线观看视频| 欧美成人免费一级人片100| 亚洲国产精品免费| 亚洲天堂久久| 国产欧美日韩亚洲精品| 香蕉视频成人在线观看| 久久一区视频| 亚洲精品网站在线播放gif| 欧美日韩ab| 午夜欧美电影在线观看| 卡一卡二国产精品| 一区二区三区免费看| 国产精品青草久久| 久久久不卡网国产精品一区| 亚洲高清av在线| 午夜精品久久久久久久白皮肤| 国产亚洲人成a一在线v站| 免费欧美在线| 亚洲欧美日韩精品久久亚洲区| 美女亚洲精品| 午夜日韩在线| 亚洲高清不卡| 国产欧美精品va在线观看| 裸体女人亚洲精品一区| 亚洲视频精选在线| 欧美激情性爽国产精品17p| 亚洲欧美国产制服动漫| 亚洲国产二区| 国产日韩欧美二区| 欧美日韩一卡二卡| 久热国产精品| 篠田优中文在线播放第一区| 亚洲高清免费| 久久久久久久综合| 亚洲一区观看| 日韩午夜电影av| 极品尤物av久久免费看| 国产精品国产三级国产专区53 | 亚洲成色www8888| 国产欧美一区二区精品性| 欧美精品一区二区精品网| 久久精品一区二区三区不卡| 亚洲视频二区| 亚洲视频免费在线| 日韩亚洲综合在线| 亚洲人成网站在线播| 欧美成人情趣视频| 久久免费精品日本久久中文字幕|