【Muduo】TcpServer類

TcpServer統領之前所有的類,是用戶直接使用的類。它通過ThreadPool管理所有的loopthread,保存所有的TcpConnection,保存用戶提供的各種回調函數并向TcpConnection的Channel中注冊回調。它負責監聽指定的端口,并接受來自客戶端的連接請求,為每個連接創建一個TcpConnection對象進行管理。它的業務邏輯較先前的三大組件來說,還是比較簡明的。

主要成員變量

  • EventLoop* loop_:指向事件循環的指針,這是由用戶創建并傳入的,是mainLoop。事件循環是Muduo網絡庫的核心組件之一,負責監聽文件描述符上的事件(如可讀、可寫、錯誤等),并調度相應的事件處理函數。
  • InetAddress listenAddr_:表示服務器監聽的地址和端口。
  • Acceptor acceptor_Acceptor對象用于監聽指定的端口,并接受來自客戶端的連接請求。當有新的連接請求到達時,Acceptor會調用相應的回調函數進行處理。
  • 回調函數TcpServer類允許用戶注冊多個回調函數,以便在特定事件發生時執行自定義邏輯。這些回調函數包括新連接到來時的回調、連接關閉時的回調等。

主要功能

  1. 監聽端口TcpServer類通過Acceptor對象監聽指定的端口,等待客戶端的連接請求。當有新的連接請求到達時,Acceptor會觸發相應的事件,通知TcpServer進行處理。
  2. 接受連接:當Acceptor接收到客戶端的連接請求時上報,TcpServer會創建一個新的TcpConnection對象來表示這個連接,并分配subLoop將其與客戶端進行關聯。同時,TcpServer會將新創建的TcpConnection對象添加到內部的管理列表中,以便后續進行管理和操作。
  3. 管理連接TcpServer類負責管理與其關聯的所有TcpConnection對象。這包括連接的建立、保持和關閉等操作。當客戶端斷開連接時,TcpServer會從管理列表中移除相應的TcpConnection對象,并釋放相關資源。
  4. 事件處理:當與TcpServer關聯的事件發生時(如新連接到來、連接關閉等),TcpServer會調用相應的回調函數進行處理。這些回調函數可以是Muduo網絡庫預定義的,也可以是用戶自定義的。通過回調函數,用戶可以編寫自己的業務邏輯來處理各種事件。
  5. 線程安全:由于Muduo網絡庫是多線程的,因此TcpServer類需要是線程安全的。這意味著在多線程環境下,多個線程可以同時訪問同一個TcpServer對象,而不會導致數據競爭或其他并發問題。Muduo網絡庫通過EventLoopThreadPool來管理所有EventLoopThread,使用互斥鎖和信號量等同步機制來保證TcpServer類的線程安全性。

使用方式

用戶通常只需要創建一個TcpServer對象,并設置相應的監聽地址和端口,然后調用其start()方法來啟動服務器。在服務器運行過程中,用戶可以通過注冊回調函數來處理各種事件。當有新的連接請求到達時,Muduo網絡庫會自動為每個連接創建一個TcpConnection對象,并將其與TcpServer進行關聯。用戶可以通過訪問與TcpServer關聯的TcpConnection對象來與客戶端進行通信和交互。當客戶端斷開連接時,Muduo網絡庫會自動從管理列表中移除相應的TcpConnection對象,并釋放相關資源。

源碼

TcpServer.h

#pragma once#include "noncopyable.h"
#include "LogStream.h"
#include "EventLoop.h"
#include "EventLoopThreadPool.h"
#include "Acceptor.h"
#include "Callbacks.h"
#include "InetAddress.h"
#include "Buffer.h"
#include "Timestamp.h"
#include "TcpConnection.h"#include <functional>
#include <string>
#include <memory>
#include <unordered_map>
#include <atomic>// 對外的服務器編程使用的類
class TcpServer : noncopyable
{
public:using ThreadInitCallback = std::function<void(EventLoop *)>;enum Option{kNoReusePort,kReusePort,};TcpServer(EventLoop *loop,const InetAddress &listenAddr,const std::string &nameArg,Option option = kNoReusePort);~TcpServer();const std::string &ipPort() const { return ipPort_; }const std::string &name() const { return name_; }EventLoop *getLoop() const { return loop_; }void setThreadNum(int numThreads);//std::shared_ptr<EventLoopThreadPool> threadPool(){return threadPool_;}/// Starts the server if it's not listening.void start();void setThreadInitCallback(const ThreadInitCallback &cb){threadInitCallback_ = cb;}void setConnectionCallback(const ConnectionCallback &cb){connectionCallback_ = cb;}void setMessageCallback(const MessageCallback &cb){messageCallback_ = cb;}void setWriteCompleteCallback(const WriteCompleteCallback &cb){writeCompleteCallback_ = cb;}private:void newConnection(int sockfd, const InetAddress &peerAddr);void removeConnection(const TcpConnectionPtr &conn);void removeConnectionInLoop(const TcpConnectionPtr &conn);using ConnectionMap = std::unordered_map<std::string, TcpConnectionPtr>;EventLoop *loop_; // the acceptor loop, 用戶定義的loopconst std::string ipPort_;const std::string name_;std::unique_ptr<Acceptor> acceptor_;              // 運行在mainLoop,監聽新連接事件std::shared_ptr<EventLoopThreadPool> threadPool_; // one loop pre threadConnectionCallback connectionCallback_;       // 有新鏈接時的回調MessageCallback messageCallback_;             // 有讀寫消息時的回調WriteCompleteCallback writeCompleteCallback_; // 消息發送完成以后的回調ThreadInitCallback threadInitCallback_;       // loop線程初始化的回調std::atomic_int started_;int nextConnId_;ConnectionMap connections_; // 保存所有的連接
};

TcpServer.cc

#include "TcpServer.h"#include <sys/socket.h>
#include <string.h>static EventLoop *checkLoopNotNull(EventLoop *loop)
{if (loop == nullptr){LOG_FATAL << "mainLoop is null!";}return loop;
}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)), // create sokect, listenthreadPool_(new EventLoopThreadPool(loop, name_)),connectionCallback_(),messageCallback_(),nextConnId_(1),started_(0)
{// 當有新用戶鏈接時,將執行TcpServer::newConnection回調:// 根據輪訓算法選擇一個subLoop并喚醒,把當前connfd封裝成channel分發給subloopacceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this,std::placeholders::_1, std::placeholders::_2));
}TcpServer::~TcpServer()
{LOG_INFO << "TcpServer::~TcpServer [" << name_ << "] destructing";for (auto &item : connections_){// 強智能指針不會再指向原來的對象,放手了,出了作用域,可以自動釋放資源TcpConnectionPtr conn(item.second);item.second.reset(); // 銷毀連接conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));}
}void TcpServer::setThreadNum(int numThreads)
{threadPool_->setThreadNum(numThreads);
}void TcpServer::start()
{LOG_DEBUG << "TcpServer::start() started_ = " << started_ << "loop_=" << loop_;if (started_++ == 0)// 防止一個sever對象被start多次{ threadPool_->start(threadInitCallback_); // 啟動底層的線程池loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get())); // 注冊AcceptorChannel到baseLoopLOG_DEBUG << "TcpServer::start success!";} 
}// 有新客戶端連接,運行在主線程mainLoop的acceptor會執行這個回調操作
void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{// 輪詢選擇一個subloop(子線程),正在epoll上阻塞,需要queueInLoopEventLoop *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 [" << name_<< "] - new connection [" << connName<< "] from " << peerAddr.toIpPort();// 通過sockfd獲取其綁定的本機的ip地址和端口信息sockaddr_in localaddr;memset(&localaddr, 0, sizeof localaddr);// ::bzero(&localaddr, sizeof localaddr);socklen_t addrlen = sizeof localaddr;if (::getsockname(sockfd, (sockaddr *)&localaddr, &addrlen) < 0){LOG_ERROR << "sockets::getLocalAddr error, errno=" << errno;}InetAddress localAddr(localaddr);// 根據連接成功的sockfd,創建TcpConnectionTcpConnectionPtr conn(new TcpConnection(ioLoop,connName,sockfd,localAddr,peerAddr));connections_[connName] = conn;// 下面的回調都是用戶來設置給TcpServer=》TcpConnection=》Channel注冊=》Poller=》notify Channel回調conn->setConnectionCallback(connectionCallback_);conn->setMessageCallback(messageCallback_);conn->setWriteCompleteCallback(writeCompleteCallback_);conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));// 直接調用TcpConnection::connectEstablished// 本函數運行在baseLoop,而ioloop在子線程阻塞在epoll_wait,// 此時在baseLoop中進行函數調用runInLoop、queueInLoop進而保存到ioloop的成員變量pendingFunctors_,// 進而一直在baseLoop中調用到ioloop的weakup函數,向ioloop的wakeupfd中寫一個數據// 在子線程中的epoll_wait監聽到寫事件,就可以返回,才能夠執行這里的回調函數ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}void TcpServer::removeConnection(const TcpConnectionPtr &conn)
{loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}void TcpServer::removeConnectionInLoop(const TcpConnectionPtr &conn)
{LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_<< "] - connection " << conn->name();connections_.erase(conn->name());EventLoop *ioLoop = conn->getLoop(); // 獲取conn所在的loop ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/14688.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14688.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14688.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ZeRO-3、模型并行、流水線并行適用情況

ZeRO-3 適用場景&#xff1a;參數量大但計算量相對均衡的情況。 主要特點&#xff1a; 參數分片&#xff1a;將模型參數、優化器狀態和梯度在多個 GPU 上進行分片。顯存優化&#xff1a;顯著減少每個 GPU 上的顯存占用&#xff0c;使得可以在較小的 GPU 上訓練更大的模型。 …

思科模擬器--06.單臂路由升級版--多端路由互連實驗--24.5.20

實驗圖紙如下: 第0步: 先放置六臺個人電腦,一臺交換機和一臺2911路由器(千兆路由器(G0開頭的)) 接著,用直通線將 PC0的F0,PC1的F0分別和交換機的F0/0, F0/1連接 交換機的F0/3和路由器的G0/0連接 PC2的F0,PC3的F0分別和交換機的F0/4, F0/5連接 交換機的F0/6和路由器的G0/1…

電腦連接愛快iKuai軟路由之后,網卡沒有正常獲取到IP,無法訪問愛快路由管理頁?

前言 上一次咱們說到在愛快控制臺上設置/辨認lan口&#xff0c;設置完成之后&#xff0c;其他的一些設置就需要在愛快iKuai軟路由的管理頁面上設置。 有些小伙伴會發現&#xff0c;當電腦連接上愛快軟路由的lan口之后&#xff0c;電腦并沒有正常獲取到ip&#xff0c;導致無法訪…

JavaScript表達式和運算符

表達式 表達式一般由常量、變量、運算符、子表達式構成。最簡單的表達式可以是一個簡單的值。常量或變量。例&#xff1a;var a10 運算符 運算符一般用符號來表示&#xff0c;也有些使用關鍵字表示。運算符由3中類型 1.一元運算符&#xff1a;一個運算符能夠結合一個操作數&…

【Arthas】阿里的線上jvm監控診斷工具的基本使用

關于對運行中的項目做java監測的需求下&#xff0c;Arthas則是一個很好的解決方案。 我們可以用來 1.監控cpu 現成、內存、堆棧 2.排查cpu飚高 造成原因 3.接口沒反應 是否死鎖 4.接口慢優化 5.代碼未按預期執行 是分支不對 還是沒提交&#xff1f; 6.線上低級錯誤 能不能不重啟…

STL--set和multiset集合

set和multiset會根據特定的排序準則&#xff0c;自動將元素排序。兩者不同之處在于multiset 允許元素重復而 set 不允許。如下圖: 使用set或multiset&#xff0c;必須先包含頭文件: #include <set>上述兩個類型都被定義為命名空間std內的class template: namespace std…

亞馬遜自養號測評:深入解析與搭建要求

在亞馬遜這電商平臺上&#xff0c;商品的評價對于賣家來說至關重要。為了提升商品的曝光率、排名、權重和銷量&#xff0c;賣家們紛紛采用各種推廣方式&#xff0c;其中&#xff0c;亞馬遜自養號測評成為了越來越多賣家選擇的一種有效方式。 亞馬遜自養號測評&#xff0c;顧名…

Android Retrofit 封裝模版

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 一、加上網絡訪問的權限二、引入依賴三、由API生成JavaBean四、封裝Retrofit五、調用 一、加上網絡訪問的權限 <uses-permission android:name"android.p…

分布式事務——9種解決方案的原理與分類

目錄 一、概要1. 分布式事務的概念2. 分布式事務解決方案分類 二、常見的分布式事務解決方案1. 基礎的 2PC&#xff08;二階段提交&#xff09;1.1 核心思想1.2 簡介1.3 主要特點1.3.1 優點1.3.2 缺點 2. 基礎的 3PC&#xff08;三階段提交&#xff09;2.1 核心思想2.2 簡介2.3…

C語言/數據結構——每日一題(有效的括號)

一.前言 如果想要使用C語言來解決這道題——有效的括號&#xff1a;https://leetcode.cn/problems/valid-parentheses/description/我們必須要借用上一篇我們所講的內容——棧的實現&#xff1a;https://blog.csdn.net/yiqingaa/article/details/138923750?spm1001.2014.3001.…

go routing 之 gorilla/mux

1. 背景 繼續學習 go 2. 關于 routing 的學習 上一篇 go 用的庫是&#xff1a;net/http &#xff0c;這次我們使用官方的庫 github.com/gorilla/mux 來實現 routing。 3. demo示例 package mainimport ("fmt""net/http""github.com/gorilla/mux&…

react實現把pc網站快捷添加到桌面快捷方式

文章目錄 1. 需求2. 實現效果3. 核心邏輯4. 完整react代碼 1. 需求 這種需求其實在國外一些游戲網站和推廣網站中經常會用到&#xff0c;目的是為了讓客戶 快捷方便的保存網站到桌面 &#xff0c;網站主動盡量避免下次找不到網站地址了&#xff0c;當然精確的客戶自己也可以使…

Python 字符串中運算符號可運行

使用eval() re {\n "path": "/sms/sendMsg",\n "data": {\n "mobile": "12345678901",\n "signCode": "短信簽名",\n "templateCode": "SMS_yyyy…

Oracle遞歸查詢筆記

目錄 一、創建表結構和插入數據 二、查詢所有子節點 三、查詢所有父節點 四、查詢指定節點的根節點 五、查詢指定節點的遞歸路徑 六、遞歸子類 七、遞歸父類 一、創建表結構和插入數據 CREATE TABLE "REGION" ( "ID" VARCHAR2(36) DEFAULT SYS_GUI…

解析Oracle文件頭內容

保存在Oracle數據文件頭中的信息很豐富&#xff0c;通常只要查詢DATAFILE_HEADER視圖就可以獲得數據文件頭中的信息。但其在數據文件頭中的具體位置&#xff0c;Oracle一直未公開過。所幸的是DBA們對數據文件頭的研究孜孜不倦&#xff0c;其研究成果在網上也是隨處可見。雖然這…

[前端|vue] 驗證器validator使用筆記 (筆記)

文檔 validator.js文檔地址 規則編寫示例 element-plus 使用示例 const captchaLoginRules {phoneNumber: [{ required: true, message: 手機號不能為空, trigger: blur },{validator: (_rule: any, value: string, _callback: any): boolean > {return isMobilePhone(…

vue-quill-editor 富文本編輯器使用出現的樣式問題

使用富文本類型&#xff1a; vue-quill-editor 注意&#xff1a; 富文本導出 html 我們使用的時候&#xff0c; 樣式凸顯不出來 DOM 結構 <p><sub class"ql-size-large">測試內容</sub><sup class"ql-size-large">222222</su…

6步:用NGINX部署ASP.NET Core,輕松上云

1. 準備工作在開始部署之前&#xff0c;確保你已經完成了以下準備工作&#xff1a;- 安裝.NET Core&#xff1a;確保你的Linux系統上安裝了.NET Core運行時。你可以從.NET官網下載。- 安裝NGINX&#xff1a;通過你的Linux發行版的包管理器安裝NGINX。例如&#xff0c;在Ubuntu上…

GPT提示詞技巧,使用教程,國內版官網直達,非套殼

GPT提示詞技巧&#xff0c;使用教程&#xff0c;國內版官網直達&#xff0c;非套殼 主站點&#xff1a;https://chatgpt-plus.top&#xff08;江蘇福建地區打不開&#xff0c;需要魔法&#xff09; 店鋪地址&#xff1a;https://buy.chatgpt-plus.top/ 選擇plus賬號進入&…

鴻蒙開發ArkUI-X基礎知識:【ArkUI代碼工程及構建介紹】

代碼工程及構建介紹 背景 ArkUI作為OpenHarmony的默認開發框架&#xff0c;在本項目&#xff08;ArkUI-X&#xff09;中需要做到一套代碼同時支持多平臺構建&#xff0c;所以會采取共倉開發的方式&#xff0c;部分倉直接指向OpenHarmony相關開源倉。 代碼結構及倉庫結構 代…