前言
對于muduo庫源碼的剖析我發現還是有些混亂的,所以這里再次梳理一下muduo網絡庫爭取可以簡單明了
首先對于muduo庫來說,不能想的得太過于復雜,它無非就是一個線程池加上epoll組成的網絡庫
這里我們從用的角度出發理解muoduo網絡庫
#include <string>#include <mymuduo/TcpServer.h>
#include <mymuduo/Logger.h>class EchoServer
{
public:EchoServer(EventLoop *loop, const InetAddress &addr, const std::string &name): server_(loop, addr, name), loop_(loop){// 注冊回調函數 將用戶定義的連接事件處理函數注冊進TcpServer中,TcpServer發生連接事件時會執行onConnection函數。server_.setConnectionCallback(std::bind(&EchoServer::onConnection, this, std::placeholders::_1));//將用戶定義的可讀事件處理函數注冊進TcpServer中,TcpServer發生可讀事件時會執行onMessage函數。server_.setMessageCallback(std::bind(&EchoServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// 設置合適的subloop線程數量 你這里設置為3,就和概述篇圖中的EventLoop2 EventLoop3 EventLoop4對應,有三個sub EventLoopserver_.setThreadNum(3);}void start(){server_.start();}private:// 連接建立或斷開的回調函數void onConnection(const TcpConnectionPtr &conn) {if (conn->connected()){LOG_INFO("Connection UP : %s", conn->peerAddress().toIpPort().c_str());}else{LOG_INFO("Connection DOWN : %s", conn->peerAddress().toIpPort().c_str());}}// 可讀寫事件回調void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp time){std::string msg = buf->retrieveAllAsString();conn->send(msg);conn->shutdown(); // 關閉寫端 底層響應EPOLLHUP => 執行closeCallback_}EventLoop *loop_;TcpServer server_;
};int main() {EventLoop loop;InetAddress addr(8002, "192.168.194.130"); //InetAddress其實是對socket編程中的sockaddr_in進行封裝,使其變為更友好簡單的接口而已EchoServer server(&loop, addr, "EchoServer");server.start(); //啟動TcpServer服務器loop.loop(); //執行EventLoop::loop()函數,這個函數在概述篇的EventLoop小節有提及return 0;
}
直接看main函數,TcpServer對象先創建出來,也就是代碼中的server
這里是它的構造函數,也就是說我們構建的TcpServer對象先創建了一個 Acceptor對象和一個線程池對象
對于Acceptor對象來說
創建了一個Channel,同時創建一個非阻塞的套接字fd給這個acceptChannel_
同時也綁定了Acceptor的回調函數,當改fd上有事發生的時候會調用這個handleRead進行相應的處理
這里就結束了,然后是線程池的創建,這個沒什么好說的,就是創建線程以及相對于的輪詢算法的方法
再看到開始的使用代碼,接下來就是Server.start
這里主要是線程池對象開始,這里會選擇一個線程,很明顯現在只有主線程的epoll運行,所有這里的loop_是mainLoop,接著調用runinLoop,然后會執行Acceptord::lisetn
這個函數就是去監聽有沒有讀事件觸發,同時把Channel注冊到mainloop的epoll里面去,如果有事件發生那么就會調用Channel注冊的回調函數,這個回調函數會調用Acceptor的handleRead
調用這個之后會拿到事件的fd,并且嗲用newConnectionCallback_這個函數,這個函數是它的上層注冊的,也就是TcpServer,注冊的一個回調函數
?所有進入到newConnection里面去
這個函數的主要目的就是把回調函數注冊進Channel,并且把傳進來的fd也給Channel,然后通過weakFd通知子loop的線程蘇醒,這里線程池會通過輪詢的方式把這個連接給到相應的線程里面去,這樣也就實現了一個線程一個loop
給到子loop,子loop同樣會去事件循環監聽事件fd有沒有事件發生,從而調用相應的回調函數(這這里的回調是用戶給予的)
這里有一張流程圖給予理解