最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

muduo源碼分析之TcpServer模塊詳細(xì)介紹

 更新時(shí)間:2022年04月25日 11:47:05   作者:小坤學(xué)習(xí)園  
這篇文章主要介紹了muduo源碼分析之TcpServer模塊,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

這次我們開始muduo源代碼的實(shí)際編寫,首先我們知道muduoLT模式,Reactor模式,下圖為Reactor模式的流程圖[來源1]

然后我們來看下muduo的整體架構(gòu)[來源1]

首先muduo有一個(gè)主反應(yīng)堆mainReactor以及幾個(gè)子反應(yīng)堆subReactor,其中子反應(yīng)堆的個(gè)數(shù)由用戶使用setThreadNum函數(shù)設(shè)置,mainReactor中主要有一個(gè)Acceptor,當(dāng)用戶建立新的連接的時(shí)候,Acceptor會(huì)將connfd和對應(yīng)的事件打包為一個(gè)channel然后采用輪詢的算法,指定將該channel給所選擇的subReactor,以后該subReactor就負(fù)責(zé)該channel的所有工作。

TcpServer類

我們按照從上到下的思路進(jìn)行講解,以下內(nèi)容我們按照一個(gè)簡單的EchoServer的實(shí)現(xiàn)思路來講解,我們知道當(dāng)我們自己實(shí)現(xiàn)一個(gè)Server的時(shí)候,會(huì)在構(gòu)造函數(shù)中實(shí)例化一個(gè)TcpServer

EchoServer(EventLoop *loop,
           const InetAddress &addr, 
           const std::string &name)
    : server_(loop, addr, name)
        , loop_(loop)
    {
        // 注冊回調(diào)函數(shù)
        server_.setConnectionCallback(
            std::bind(&EchoServer::onConnection, this, std::placeholders::_1)
        );

        server_.setMessageCallback(
            std::bind(&EchoServer::onMessage, this,
                      std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)
        // 設(shè)置合適的loop線程數(shù)量 loopthread 不包括baseloop
        server_.setThreadNum(3);
    }

于是我們?nèi)タ聪?code>TcpServer的構(gòu)造函數(shù)是在干什么

TcpServer::TcpServer(EventLoop *loop,
                const InetAddress &listenAddr,
                const std::string &nameArg,
                Option option)
                : loop_(CheckLoopNotNull(loop))
                , ipPort_(listenAddr.toIpPort())
                , name_(nameArg)
                , acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))
                , threadPool_(new EventLoopThreadPool(loop, name_))
                , connectionCallback_()
                , messageCallback_()
                , nextConnId_(1)
                , started_(0)
{
    // 當(dāng)有新用戶連接時(shí)候,會(huì)執(zhí)行該回調(diào)函數(shù)
    acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, 
        std::placeholders::_1, std::placeholders::_2));
}

我們只需要關(guān)注acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))threadPool_(new EventLoopThreadPool(loop, name_))
首先很明確的一點(diǎn),構(gòu)造了一個(gè)Acceptor,我們首先要知道Acceptor主要就是連接新用戶并打包為一個(gè)Channel,所以我們就應(yīng)該知道Acceptor按道理應(yīng)該實(shí)現(xiàn)socket,bind,listenaccept這四個(gè)函數(shù)。

Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport)
    : loop_(loop), acceptSocket_(createNonblocking()) // socket
      ,
      acceptChannel_(loop, acceptSocket_.fd()), listenning_(false)
{
    acceptSocket_.setReuseAddr(true);
    acceptSocket_.setReusePort(true);
    acceptSocket_.bindAddress(listenAddr); // 綁定套接字
    // 有新用戶的連接,執(zhí)行一個(gè)回調(diào)(打包為channel)
    acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));
}

其中Acceptor中有個(gè)acceptSocket_,其實(shí)就是我們平時(shí)所用的listenfd,構(gòu)造函數(shù)中實(shí)現(xiàn)了socketbind,而其余的兩個(gè)函數(shù)的使用在其余代碼

// 開啟服務(wù)器監(jiān)聽
void TcpServer::start()
{
	// 防止一個(gè)TcpServer被start多次
    if (started_++ == 0) 
    {
        threadPool_->start(threadInitCallback_); // 啟動(dòng)底層的loop線程池,這里會(huì)按照設(shè)定了threadnum設(shè)置pool的數(shù)量
        loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));
    }
}

我們知道,當(dāng)我們設(shè)置了threadnum之后,就會(huì)有一個(gè)mainloop,那么這個(gè)loop_就是那個(gè)mainloop,其中可以看見這個(gè)loop_就只做一個(gè)事情Acceptor::listen。

void Acceptor::listen()
{
    listenning_ = true;
    acceptSocket_.listen();         // listen
    acceptChannel_.enableReading(); // acceptChannel_ => Poller
}

這里就實(shí)現(xiàn)了listen函數(shù),還有最后一個(gè)函數(shù)accept,我們慢慢向下分析,從代碼可以知道acceptChannel_.enableReading()之后就會(huì)使得這個(gè)listenfd所在的channel對讀事件感興趣,那什么時(shí)候會(huì)有讀事件呢,就是當(dāng)用戶建立新連接的時(shí)候,那么我們應(yīng)該想一下,那當(dāng)感興趣的事件發(fā)生之后,listenfd應(yīng)該干什么呢,應(yīng)該執(zhí)行一個(gè)回調(diào)函數(shù)呀。注意Acceptor構(gòu)造函數(shù)中有這樣一行代碼acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));這就是那個(gè)回調(diào),我們?nèi)タ聪?code>handleRead在干嘛。

// listenfd有事件發(fā)生了,就是有新用戶連接了
void Acceptor::handleRead()
{
    InetAddress peerAddr;
    int connfd = acceptSocket_.accept(&peerAddr);
    if (connfd >= 0)
    {
        // 若用戶實(shí)現(xiàn)定義了,則執(zhí)行,否則說明用戶對新到來的連接沒有需要執(zhí)行的,所以直接關(guān)閉
        if (newConnectionCallback_)
        {
            newConnectionCallback_(connfd, peerAddr); // 輪詢找到subLoop,喚醒,分發(fā)當(dāng)前的新客戶端的Channel
        }
        else
        {
            ::close(connfd);
        }
    }
    ...
}

這里是不是就實(shí)現(xiàn)了accept函數(shù),至此當(dāng)用戶建立一個(gè)新的連接時(shí)候,Acceptor就會(huì)得到一個(gè)connfd和其對應(yīng)的peerAddr返回給mainloop,這時(shí)候我們在注意到TcpServer構(gòu)造函數(shù)中有這樣一行代碼acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this,std::placeholders::_1, std::placeholders::_2));我們給acceptor_設(shè)置了一個(gè)newConnectionCallback_,于是由上面的代碼就可以知道,if (newConnectionCallback_)為真,就會(huì)執(zhí)行這個(gè)回調(diào)函數(shù),于是就會(huì)執(zhí)行TcpServer::newConnection,我們?nèi)タ聪逻@個(gè)函數(shù)是在干嘛。

void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{
    // 輪詢算法選擇一個(gè)subloop來管理對應(yīng)的這個(gè)新連接
    EventLoop *ioLoop = threadPool_->getNextLoop(); 
    char buf[64] = {0};
    snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
    ++nextConnId_;
    std::string connName = name_ + buf;

    LOG_INFO("TcpServer::newConnection [%s] - new connection [%s] from %s \n",
        name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str());
    // 通過sockfd獲取其綁定的本地ip和端口
    sockaddr_in local;
    ::bzero(&local, sizeof local);
    socklen_t addrlen = sizeof local;
    if (::getsockname(sockfd, (sockaddr*)&local, &addrlen) < 0)
    {
        LOG_ERROR("sockets::getLocalAddr");
    }
    InetAddress localAddr(local);
    // 根據(jù)連接成功的sockfd,創(chuàng)建TcpConnection
    TcpConnectionPtr conn(new TcpConnection(
                            ioLoop,
                            connName,
                            sockfd,   // Socket Channel
                            localAddr,
                            peerAddr));
    connections_[connName] = conn;
	// 下面的回調(diào)時(shí)用戶設(shè)置給TcpServer,TcpServer又設(shè)置給TcpConnection,TcpConnetion又設(shè)置給Channel,Channel又設(shè)置給Poller,Poller通知channel調(diào)用這個(gè)回調(diào)
    conn->setConnectionCallback(connectionCallback_);
    conn->setMessageCallback(messageCallback_);
    conn->setWriteCompleteCallback(writeCompleteCallback_);
    // 設(shè)置了如何關(guān)閉連接的回調(diào)
    conn->setCloseCallback(
        std::bind(&TcpServer::removeConnection, this, std::placeholders::_1)
    );
    // 直接調(diào)用connectEstablished
    ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

這里就比較長了,我先說下大概他干了啥事情:首先通過輪詢找到下一個(gè)subloop,然后將剛剛返回的connfd和對應(yīng)的peerAddr以及localAddr構(gòu)造為一個(gè)TcpConnectionsubloop,然后給這個(gè)conn設(shè)置了一系列的回調(diào)函數(shù),比如讀回調(diào),寫回調(diào),斷開回調(diào)等等。下一章我們來說下上面的代碼最后幾行在干嘛。

到此這篇關(guān)于muduo源碼分析之TcpServer模塊的文章就介紹到這了,更多相關(guān)muduo TcpServer模塊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • ?Redis 串行生成順序編碼的方法實(shí)現(xiàn)

    ?Redis 串行生成順序編碼的方法實(shí)現(xiàn)

    本文主要介紹了?Redis 串行生成順序編碼的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • 解決redis在linux上的部署的問題

    解決redis在linux上的部署的問題

    這篇文章主要介紹了redis在linux上的部署,本文分步驟給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • Redis實(shí)戰(zhàn)之百度首頁新聞熱榜的實(shí)現(xiàn)代碼

    Redis實(shí)戰(zhàn)之百度首頁新聞熱榜的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Redis實(shí)戰(zhàn)之百度首頁新聞熱榜的實(shí)現(xiàn)代碼,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • Windows下Redis的安裝使用圖解

    Windows下Redis的安裝使用圖解

    Redis是一個(gè)key-value存儲(chǔ)系統(tǒng)。Redis的出現(xiàn),很大程度補(bǔ)償了memcached這類key/value存儲(chǔ)的不足,在部分場合可以對關(guān)系數(shù)據(jù)庫起到很好的補(bǔ)充作用。這篇文章小編為大家分享了在Windows下進(jìn)行安裝和使用Redis的技巧。
    2015-09-09
  • Redis中的3種特殊數(shù)據(jù)結(jié)構(gòu)詳解

    Redis中的3種特殊數(shù)據(jù)結(jié)構(gòu)詳解

    在本文中,我們對三種特殊的數(shù)據(jù)類型進(jìn)行了介紹,它們分別是geospatial(地理空間數(shù)據(jù)類型)、HyperLogLogs和Bitmaps(位圖),這些數(shù)據(jù)類型在不同的領(lǐng)域和應(yīng)用中發(fā)揮著重要作用,并且具有各自獨(dú)特的特性和用途,對Redis特殊數(shù)據(jù)結(jié)構(gòu)相關(guān)知識(shí)感興趣的朋友一起看看吧
    2024-02-02
  • Redis和Lua使用過程中遇到的小問題

    Redis和Lua使用過程中遇到的小問題

    這篇文章主要給大家介紹了關(guān)于Redis和Lua使用過程中遇到的小問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Quarkus集成redis操作Redisson實(shí)現(xiàn)數(shù)據(jù)互通

    Quarkus集成redis操作Redisson實(shí)現(xiàn)數(shù)據(jù)互通

    這篇文章主要為大家介紹了Quarkus集成redis操作Redisson實(shí)現(xiàn)數(shù)據(jù)互通的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-02-02
  • Window下對Redis進(jìn)行開啟與關(guān)閉的操作方法

    Window下對Redis進(jìn)行開啟與關(guān)閉的操作方法

    這篇文章主要介紹了Window下對Redis進(jìn)行開啟與關(guān)閉的操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-11-11
  • 淺談Redis高并發(fā)緩存架構(gòu)性能優(yōu)化實(shí)戰(zhàn)

    淺談Redis高并發(fā)緩存架構(gòu)性能優(yōu)化實(shí)戰(zhàn)

    本文主要介紹了淺談Redis高并發(fā)緩存架構(gòu)性能優(yōu)化實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • 詳解redis分布式鎖的這些坑

    詳解redis分布式鎖的這些坑

    在很多互聯(lián)網(wǎng)產(chǎn)品應(yīng)用中,有些場景需要加鎖處理,比如:秒殺,全局遞增ID等等。大部分的解決方案是基于DB實(shí)現(xiàn)的,Redis為單進(jìn)程單線程模式,采用隊(duì)列模式將并發(fā)訪問變成串行訪問,且多客戶端對Redis的連接并不存在競爭關(guān)系。
    2021-05-05

最新評論

西安市| 清徐县| 本溪市| 乌恰县| 乌恰县| 扶余县| 昌黎县| 伊金霍洛旗| 曲靖市| 稷山县| 板桥市| 濮阳县| 鸡西市| 水城县| 上杭县| 温州市| 博客| 凤庆县| 来安县| 镇宁| 宁海县| 鱼台县| 吴堡县| 陈巴尔虎旗| 兰考县| 山阴县| 洪洞县| 侯马市| 富锦市| 武平县| 介休市| 冀州市| 芮城县| 简阳市| 胶南市| 郓城县| 海安县| 林甸县| 施秉县| 南通市| 容城县|