muduo

好的,我們來深入剖析陳碩老師開發的著名C++網絡庫——muduo。它以“簡單、高效、易用”著稱,是學習Linux C++高性能網絡編程的絕佳范本。我會盡量詳細、通俗地講解其核心思想、關鍵組件、源碼結構和工作原理。

核心思想:Reactor 模式 (Non-blocking + I/O Multiplexing)

muduo?的靈魂是?Reactor 模式。理解它就理解了?muduo?的一半。想象一下:

  1. 傳統阻塞模型的問題:?想象一個餐廳只有一個服務員。每次點菜、上菜、結賬,服務員都要等顧客做完一件事才能服務下一個(阻塞)。效率極低,顧客(連接)一多就完蛋。

  2. Reactor 模型的解決方案:?餐廳安裝了一個呼叫鈴系統(epoll/poll/kqueue)。服務員(主線程)只需要坐在前臺,盯著一個大屏幕(事件循環?EventLoop),哪個桌子的鈴響了(文件描述符?fd?就緒了),服務員就去處理哪個桌子的需求(回調函數)。服務員永遠不會傻等

  3. 關鍵點:

    • 非阻塞 I/O (Non-blocking I/O):?所有網絡操作(accept,?read,?write,?connect)都設置成非阻塞。調用它們會立即返回,如果數據沒準備好(比如?read?時緩沖區空),就返回一個錯誤(EAGAIN?或?EWOULDBLOCK),而不是傻等。

    • I/O 多路復用 (I/O Multiplexing):?使用?epoll(Linux 首選)、poll?或?select(效率低,不推薦)來同時監聽大量文件描述符(fd)上的事件(可讀、可寫、錯誤等)。當任何一個被監聽的?fd?上有事件發生時,多路復用器會通知程序。

    • 事件驅動 (Event-Driven):?程序的核心是一個事件循環 (Event Loop)。它不斷地詢問多路復用器:“哪些?fd?有事件了?”。然后,它根據?fd?上發生的事件類型(讀、寫),調用預先注冊好的回調函數 (Callback)?來處理這些事件(比如讀取數據、發送數據、接受新連接)。

muduo?的主要實現方法和核心組件

muduo?將 Reactor 模式拆解并封裝成幾個核心類,它們協同工作:

  1. EventLoop?(事件循環): 這是 Reactor 模式的心臟和發動機。

    • 職責:?每個?EventLoop?對象在一個線程中運行,負責不斷執行以下任務:

      • 調用?Poller?獲取就緒的事件列表。

      • 遍歷就緒事件列表。

      • 根據事件關聯的?Channel?對象,調用相應的讀/寫回調函數。

      • 處理定時器到期事件。

      • 執行其他線程通過?runInLoop?提交過來的函數(跨線程調用)。

    • 關鍵成員:

      • Poller* poller_:指向具體的 I/O 多路復用器實現(EPollPoller?或?PollPoller)。

      • ChannelList activeChannels_:存放本次循環中有事件發生的?Channel

      • TimerQueue timerQueue_:管理定時器。

      • int wakeupFd_?+?Channel wakeupChannel_:用于喚醒阻塞在?Poller::poll()?上的事件循環(例如其他線程有任務要提交)。

    • 源碼關鍵方法 (loop.cc):

      • loop():核心循環函數,調用?poll?-> 填充?activeChannels_?-> 處理每個?Channel?的事件 (handleEvent) -> 處理定時器 -> 執行?pendingFunctors_

      • runInLoop(const Functor& cb),?queueInLoop(const Functor& cb):安全地跨線程向?EventLoop?提交任務。

      • updateChannel(Channel*),?removeChannel(Channel*):管理?Poller?監聽的?Channel

  2. Channel?(通道): 事件分派器。它是?EventLoop?與具體文件描述符之間的橋梁。

    • 職責:?封裝一個文件描述符 (fd) 及其感興趣的事件?(讀、寫等) 和?事件發生時的回調函數。它是事件處理的最小單位

    • 關鍵成員:

      • int fd_:它負責的文件描述符(socket, eventfd, timerfd, signalfd 等)。

      • int events_:它關心的事件(EPOLLIN,?EPOLLOUT,?EPOLLPRI,?EPOLLERR,?EPOLLHUP)。

      • int revents_:由?Poller::poll()?設置,表示?fd_?上實際發生的事件。

      • ReadEventCallback readCallback_:可讀事件回調。

      • EventCallback writeCallback_:可寫事件回調。

      • EventCallback closeCallback_:關閉事件回調。

      • EventCallback errorCallback_:錯誤事件回調。

      • EventLoop* loop_:它所屬的?EventLoop

    • 源碼關鍵方法 (Channel.cc):

      • handleEvent(Timestamp receiveTime):核心方法!被?EventLoop::loop()?調用。根據?revents_?的值,判斷發生了什么事件(讀?寫?錯誤?關閉?),然后調用對應的回調函數。這是事件處理的最終落腳點。

      • enableReading(),?enableWriting(),?disableWriting(),?disableAll():設置/修改?events_,并調用?update()?通知?EventLoop?更新?Poller?的監聽。

      • update():內部調用?EventLoop::updateChannel(this)

  3. Poller?(輪詢器): I/O 多路復用的抽象層。

    • 職責:?封裝底層 I/O 多路復用系統調用(epoll_wait,?poll)。負責監聽一組?Channel(通過其?fd_?和?events_),并在事件發生時填充?revents_?并返回有事件發生的?Channel?列表給?EventLoop

    • 多態實現:

      • EPollPoller?(Linux 首選):使用高效的?epoll

      • PollPoller:使用傳統的?poll(效率較低,作為備選)。

    • 關鍵成員 (EPollPoller.cc):

      • int epollfd_epoll_create?創建的描述符。

      • std::vector epoll_events_:存放?epoll_wait?返回的就緒事件。

    • 源碼關鍵方法:

      • poll(int timeoutMs, ChannelList* activeChannels):調用?epoll_wait/poll,將就緒的事件對應的?Channel?找出,設置其?revents_,并放入?activeChannels?列表返回給?EventLoop

      • updateChannel(Channel*),?removeChannel(Channel*):向?epollfd_?添加、修改或刪除對某個?fd?(Channel) 的監聽。

  4. Acceptor?(接受器): 專門處理新連接接入。

    • 職責:?封裝監聽套接字 (listening socket) 的?Channel。當監聽套接字可讀(有新連接到來)時,調用其回調函數?handleRead()。在?handleRead()?中,調用?accept?接受新連接,然后調用用戶注冊的?NewConnectionCallback?(通常是?TcpServer?提供的)。

    • 位置:?Acceptor?通常由?TcpServer?擁有和使用。

    • 源碼關鍵方法 (Acceptor.cc):

      • handleRead():核心方法。調用?accept?獲取新連接的?connfd,創建?InetAddress?表示客戶端地址,然后調用?newConnectionCallback_(connfd, peerAddr)

  5. TcpConnection?(TCP 連接): 已建立連接的抽象。這是用戶與網絡交互的核心對象。

    • 職責:?封裝一個已建立的 TCP 連接的生命周期。它包含:

      • 該連接對應的?Socket?對象 (封裝?connfd)。

      • 該連接對應的?Channel?對象 (用于在?EventLoop?中監聽?connfd?的事件)。

      • 輸入緩沖區 (inputBuffer_):?應用層接收緩沖區。當?Channel?的可讀回調被調用時,從?connfd?讀取數據追加到?inputBuffer_,然后調用用戶設置的?MessageCallback。用戶處理的是?inputBuffer_?里的數據。

      • 輸出緩沖區 (outputBuffer_):?應用層發送緩沖區。用戶調用?send?或?write?時,數據先寫入?outputBuffer_。如果?connfd?當前可寫,則嘗試直接從?outputBuffer_?向內核發送數據;如果內核發送緩沖區滿(write?返回?EAGAIN)或?outputBuffer_?還有數據沒發完,則通過?Channel?監聽?EPOLLOUT?事件。當可寫事件發生時,繼續嘗試發送?outputBuffer_?中的數據,發完后取消監聽?EPOLLOUT

      • 各種回調函數 (ConnectionCallback,?MessageCallback,?WriteCompleteCallback,?CloseCallback):由用戶設置,在連接建立、收到消息、數據發送完成、連接關閉時被調用。

    • 核心思想 - 緩沖區 (Buffer):?muduo?采用?應用層緩沖區?是高性能網絡庫的關鍵設計。它解耦了網絡 I/O 的速率與用戶處理邏輯的速率inputBuffer_?允許 TCP 粘包處理由用戶決定;outputBuffer_?允許用戶在任何時候調用?send(即使內核緩沖區暫時不可寫),避免阻塞用戶線程。

    • 源碼關鍵方法 (TcpConnection.cc):

      • handleRead(Timestamp)Channel?的可讀回調。從?socket_?讀取數據到?inputBuffer_,調用?messageCallback_

      • handleWrite()Channel?的可寫回調。嘗試將?outputBuffer_?中的數據寫入?socket_。如果寫完了,取消監聽?EPOLLOUT,調用?writeCompleteCallback_;如果沒寫完,繼續監聽?EPOLLOUT

      • handleClose(),?handleError():處理關閉和錯誤。

      • send(const void* message, size_t len),?send(const StringPiece& message),?send(Buffer*):用戶發送數據的接口。核心邏輯是將數據放入?outputBuffer_,然后嘗試立即發送(如果?Channel?沒有在監聽?EPOLLOUT?且?outputBuffer_?之前為空),否則會觸發后續的?handleWrite

      • shutdown(),?forceClose():關閉連接。

  6. TcpServer?(TCP 服務器): 管理整個服務器生命周期。

    • 職責:?組合上述組件,提供用戶友好的服務器接口。

      • 持有?Acceptor?對象監聽新連接。

      • 持有?EventLoopThreadPool?線程池(可選)。

      • 管理所有存活的?TcpConnection?(std::map)。

      • 設置各種回調 (ConnectionCallback,?MessageCallback?等) 并傳遞給新建的?TcpConnection

    • 多線程模型 (EventLoopThreadPool):

      • IO線程:?運行?EventLoop?的線程。負責處理 I/O 事件(accept,?read,?write)。TcpServer?的?EventLoop?(通常叫?baseloop_) 運行?Acceptor。新建的?TcpConnection?的?EventLoop?由線程池分配。

      • 計算線程池 (可選):?如果業務邏輯計算密集,用戶可以在?MessageCallback?中將接收到的數據?inputBuffer_?傳遞給計算線程池處理,處理完后再通過?runInLoop?將結果交還給該連接的 IO 線程,通過?TcpConnection::send?發送。IO 線程只做 I/O,計算線程只做計算,避免計算阻塞 I/O。

    • 源碼關鍵方法 (TcpServer.cc):

      • start():啟動服務器。啟動線程池(如果設置了),讓?Acceptor?開始監聽。

      • newConnection(int sockfd, const InetAddress& peerAddr)Acceptor?的?NewConnectionCallback。創建?TcpConnection?對象,選擇一個?EventLoop?(IO線程) 管理它,設置好各種回調,并加入到?connectionMap_

      • removeConnection(const TcpConnectionPtr& conn)TcpConnection::CloseCallback。從?connectionMap_?移除連接。注意:?移除操作必須在?conn?所屬的 IO 線程中執行(通過?runInLoop?保證)。

  7. Buffer?(緩沖區): 應用層緩沖區,核心數據結構。

    • 設計:?muduo::net::Buffer?是一個非線程安全的、基于?std::vector?的動態增長緩沖區。它采用?“讀指針”和“寫指針”?(內部用索引實現) 的設計,避免頻繁的內存拷貝。

    • 內存布局:

      text

      [Prependable Bytes] [Readable Bytes] [Writable Bytes]
      |                 |                |               |
      0           readerIndex_   writerIndex_      size()
      • Prependable Bytes:?預留空間,方便在數據前面添加頭部(如長度字段)。

      • Readable Bytes:?readerIndex_?到?writerIndex_?之間的數據,是待用戶讀取/處理的有效數據 (inputBuffer_) 或待發送的數據 (outputBuffer_)。

      • Writable Bytes:?writerIndex_?到?size()?之間的空間,可寫入新數據。

    • 關鍵操作 (Buffer.cc):

      • retrieve(size_t len):用戶讀取了?len?字節后調用,移動?readerIndex_

      • retrieveAll():移動?readerIndex_?和?writerIndex_?到初始位置(可能回收內存)。

      • append(const char* data, size_t len),?append(const void* data, size_t len):向 Writable 區域寫入數據,移動?writerIndex_

      • prepend(const void* data, size_t len):向 Prependable 區域寫入數據,移動?readerIndex_?(向前)。

      • readFd(int fd, int* savedErrno):核心!從?fd?讀取數據到 Buffer 的 Writable 區域。如果空間不夠,Buffer 會自動擴容。使用?readv?系統調用進行分散讀 (Scatter Read),先讀到 Buffer 的 Writable 空間,如果不夠,再讀到棧上的臨時緩沖區,最后?append?到 Buffer。高效地利用了內存和系統調用。

      • writeFd(int fd, int* savedErrno):核心!將 Readable 區域的數據寫入?fd。使用?write?系統調用。

muduo?的設計哲學與優勢

  1. One Loop Per Thread + ThreadPool:

    • 每個 IO 線程運行一個?EventLoop

    • Acceptor?在?main loop?中。

    • 新連接?TcpConnection?被分配到某個?IO loop

    • 計算任務交給單獨的線程池。

    • 優點:?充分利用多核;避免鎖競爭(每個連接的數據只在其 IO 線程內操作);結構清晰。

  2. Non-Blocking + Buffer:

    • 所有 I/O 操作都是非阻塞的。

    • 使用應用層緩沖區 (Buffer) 解耦 I/O 速率與處理速率,這是高性能的關鍵。

  3. 基于事件回調 (Event Callback):

    • 通過函數對象 (std::function) 實現高度靈活性。用戶只需注冊關心的回調函數。

  4. 資源管理:shared_ptr?+?weak_ptr

    • TcpConnection?的生命期由?shared_ptr?管理。當?Channel?觸發關閉事件時,TcpConnection?的回調最終會將其從?TcpServer?的?connectionMap_?中移除并銷毀。weak_ptr?用于跨線程安全地訪問?TcpConnection

  5. RAII (Resource Acquisition Is Initialization):

    • 大量使用 RAII 管理資源(文件描述符?Socket、內存、鎖?MutexLockGuard),確保異常安全。

  6. 簡單即美:

    • 避免過度設計。核心類職責明確,接口清晰。源碼相對容易閱讀(對于 C++ 網絡庫而言)。

如何使用?muduo?(一個簡單 EchoServer 示例)

cpp

#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/base/Logging.h>using namespace muduo;
using namespace muduo::net;void onConnection(const TcpConnectionPtr& conn) {if (conn->connected()) {LOG_INFO << "New Connection: " << conn->peerAddress().toIpPort();} else {LOG_INFO << "Connection Closed: " << conn->peerAddress().toIpPort();}
}void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) {// 接收到的數據在 buf 中string msg(buf->retrieveAllAsString()); // 取出所有數據LOG_INFO << "Received " << msg.size() << " bytes from " << conn->peerAddress().toIpPort();conn->send(msg); // 原樣發回 (Echo)
}int main() {EventLoop loop; // Main EventLoopInetAddress listenAddr(8888);TcpServer server(&loop, listenAddr, "EchoServer");// 設置回調函數server.setConnectionCallback(onConnection);server.setMessageCallback(onMessage);server.start(); // 啟動監聽loop.loop();    // 啟動事件循環 (阻塞在此)return 0;
}

剖析一下這個例子如何映射到?muduo?組件:

  1. EventLoop loop;:創建主事件循環。

  2. TcpServer server(...);:創建?TcpServer

    • 內部創建?Acceptor?監聽端口 8888。

    • Acceptor?的?Channel?注冊到?loop?上監聽?EPOLLIN?(新連接)。

  3. server.setXxxCallback():設置用戶回調。

  4. server.start():啟動?Acceptor?開始監聽。

  5. loop.loop():啟動事件循環。

    • 當有新連接 (Acceptor?的?Channel?可讀),Acceptor::handleRead()?被調用 ->?accept?-> 創建?TcpConnection?對象?conn?-> 選擇一個?IO loop?-> 在該?IO loop?中注冊?conn?的?Channel?-> 設置?conn?的回調 (onConnection,?onMessage) -> 將?conn?加入?TcpServer?的管理 map。

    • 當?conn?上有數據到來 (conn?的?Channel?可讀),TcpConnection::handleRead()?被調用 -> 讀入?inputBuffer_?-> 調用用戶?onMessage(conn, inputBuffer_, time)?-> 用戶在?onMessage?中處理數據 (buf->retrieveAllAsString()) 并調用?conn->send(msg)?->?send?將數據放入?outputBuffer_?并嘗試立即發送或注冊?EPOLLOUT

    • 當?conn?可寫時 (EPOLLOUT?觸發),TcpConnection::handleWrite()?被調用 -> 發送?outputBuffer_?中的數據。

源碼閱讀建議

  1. 從示例開始:?先編譯運行?examples?目錄下的簡單例子 (如?echo,?discard,?chargen),感受用法。

  2. 核心類入手:?重點閱讀?EventLoop,?Channel,?Poller?(EPollPoller),?TcpConnection,?Buffer?的實現。理解它們的關系和協作流程 (loop()?->?poll()?->?handleEvent()?->?readCallback_/writeCallback_?->?Buffer?操作)。

  3. 關注回調注冊與觸發:?在?TcpServer,?Acceptor,?TcpConnection?中,看回調 (std::function) 是如何被設置,并在何時被調用的。

  4. 理解?Buffer?的設計:?仔細看?Buffer::readFd?和?Buffer::writeFd?的實現,理解其高效性。

  5. 多線程模型:?研究?EventLoopThread,?EventLoopThreadPool?以及?TcpServer?如何分配新連接。注意跨線程調用的?runInLoop?機制和?wakeupFd_?的作用。

  6. RAII 與智能指針:?觀察?Socket?類如何管理?fdTcpConnection?的生命期如何通過?shared_ptr?管理,Channel?如何安全地從?EventLoop?移除。

總結

muduo?是一個將 Reactor 模式在 Linux C++ 環境下實現得精煉、高效且實用的網絡庫。其核心在于:

  • 事件驅動:?EventLoop?+?Poller?+?Channel?構成了事件處理引擎。

  • 非阻塞 I/O + 應用層緩沖區:?TcpConnection?+?Buffer?高效處理連接數據流。

  • 清晰的線程模型:?One Loop Per Thread + ThreadPool 平衡了并發與復雜度。

  • 基于回調的編程模型:?用戶只需關注連接、數據到達、數據發送完成等事件的處理邏輯。

  • RAII 與智能指針:?確保資源安全和簡化生命周期管理。

深入理解?muduo?的源碼,不僅對使用它大有裨益,更是學習 Linux 高性能服務器編程思想、C++ 網絡編程實踐和良好軟件設計模式的寶貴資源。祝你學習順利!

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

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

相關文章

將目錄下所有圖像中非0像素值改為1或者255

圖像二值化處理技術大綱 目標與背景 解釋圖像二值化的意義,分析將非零像素值統一調整為1或255的應用場景(如簡化數據、增強特征、適配模型輸入等)。 核心方法概述 列舉常見圖像格式(如PNG、JPEG)的像素值范圍,說明非零像素的定義(RGB或灰度圖像中的非黑像素)。 方…

Reactor ConnectableFlux支持多訂閱者

在 Reactor 中&#xff0c;ConnectableFlux 是一種用于處理響應式流的機制&#xff0c;它允許你控制何時開始訂閱和數據生成。通常情況下&#xff0c;訂閱者&#xff08;subscriber&#xff09;在訂閱時會立即開始接收數據&#xff0c;但有時你可能希望多個訂閱者“會面”&…

vite + vue 項目下使用 tailwindcss

版本 node: > 18.0.0 vue: 3.5.13 vite: 6.3.1 tailwindcss: 4.1.6 tailwindcss/vite: 4.1.6 tailwindcss ? 細粒度類庫 提供數千個原子級CSS類&#xff08;如 text-center、bg-blue-500、p-4&#xff09;&#x1f9e9; 組合式開發 通過類名組合構建完全自定義的UI&#x…

Hibernate中save與saveOrUpdate的差異解析

在Hibernate中&#xff0c;save()和saveOrUpdate()都是用于持久化對象的方法&#xff0c;但它們的適用場景和行為有顯著差異&#xff1a; 1. save()方法 核心行為&#xff1a; 僅適用于瞬時態&#xff08;Transient&#xff09;對象&#xff08;即新創建、未與Session關聯的對象…

香橙派3B學習筆記14:deb 打包程序_解包前后腳本運行

本文學習如何用deb打包的方式打包自己需要調用系統庫的程序。 然后實現deb解包前后的腳本運行。 目錄 承接上文&#xff1a; 刪除上文遺留的.so文件&#xff1a; 終止ledlight進程&#xff1a; 目標解釋&#xff1a; 創建項目結構&#xff1a; 創建control文件&#xff1a; 創…

nanoGPT復現——prepare拆解(自己構建詞表 VS tiktoken)

在nanoGPT的data文件夾有兩個很相似的文件夾結構&#xff1a;shakespeare和shakespeare-char&#xff0c;這兩種都是對shakespeare數據集的處理&#xff0c;但是shakespeare使用的是tiktoken對文字進行編碼&#xff0c;另一個則是使用自己構建的詞表 一、shakespeare-char&…

macos 安裝 xcode

在 macOS 上安裝 Xcode&#xff08;或者 Xcode Command Line Tools&#xff09;的方法如下&#xff1a; 1. 安裝 Xcode Command Line Tools&#xff08;輕量級&#xff0c;滿足大部分編譯需求&#xff09; 終端命令&#xff1a; xcode-select --install會彈出安裝提示&#x…

大學專業科普 | 云計算、大數據

大數據專業是近年來隨著信息技術發展而興起的熱門學科&#xff0c;專注于從海量、多樣化的數據中提取有價值信息&#xff0c;為各行業提供數據驅動的決策支持。 專業定義 大數據專業旨在培養掌握大數據采集、存儲、管理、分析和應用等核心技術的人才。該專業融合了計算機科學…

本地文件自動提交到倉庫

背景 將本地目錄做一個存儲倉庫&#xff0c;將歸檔的文件放入其中。自動同步到遠程倉庫。 倉庫配置 省略 配置密鑰 用戶可以 git pull \ git push \ git commit 自動 拉取、更新 腳本 文件名&#xff1a;autosave.sh #!/bin/zsh# 設置變量 LOCAL_DIR$1# 進入工作目錄 cd "…

Ubuntu中控制用戶存儲空間配置步驟

目的&#xff0c;限制用戶磁盤空間占用&#xff0c;例如給用戶限制100-150G容量 1.安裝磁盤配額工具 sudo apt-get install -y quota 2.備份并修改/etc/fstab文件&#xff0c;使能支持quota sudo cp /etc/fstab /etc/fstab.bak vim /etc/fstab #寫入如下,usrjquotaaquota.u…

【網絡】Linux 內核優化實戰 - net.ipv4.tcp_rmem 和 net.core.rmem_default 關系

net.ipv4.tcp_rmem 和 net.core.rmem_default 都是 Linux 內核中控制網絡接收緩沖區的參數,但它們的作用范圍、優先級和使用場景存在明顯區別。以下是詳細對比: 核心區別 參數net.ipv4.tcp_rmemnet.core.rmem_default作用協議僅針對 TCP 協議針對 所有網絡協議(TCP、UDP 等…

設計模式精講 Day 14:命令模式(Command Pattern)

【設計模式精講 Day 14】命令模式&#xff08;Command Pattern&#xff09; 文章內容 在“設計模式精講”系列的第14天&#xff0c;我們來學習命令模式&#xff08;Command Pattern&#xff09;。命令模式是一種行為型設計模式&#xff0c;它將請求封裝為對象&#xff0c;從而…

手機射頻功放測試學習(二)——手機線性功放的靜態電流和小信號(S-Parameter)測試

目錄 一、概要 二、LPA的電流測試 1、LPA的泄漏電流測試 手動測試步驟如下: 自動化測試: 2、LPA的靜態電流測試 手動測試步驟如下: 自動化測試: 三、LPA的S-Parameter測試 1、矢量網絡分析儀校準 2、LPA的S參數手動測試步驟: 3、LPA的S參數自動測試步驟: 四…

基礎算法合集-圖論

本文將介紹數據結構圖論部分中常見的算法 單源最短路徑問題(用來計算一個點到其他所有頂點的最短路徑) Dijkstra(n*n) 1. 初始化: 先找出從源點V0到各終點Vk的直達路徑(V0,Vk), 即通過一條弧到達的路徑 2. 選擇: 從這些路徑中找出一條長度最短的路徑(V0,u) 3. 更新: 然后對其余…

vue-i18n 插件打包解析失效問題記錄

vue-i18n 插件打包解析失效問題記錄 開發環境中沒有問題的&#xff0c;但打包發布之后就不行了&#xff0c;顯示的就是模板字符串 // An highlighted block const messages {en: {step: {stepDesc1: Scan,stepDesc2: Analyze,stepDesc3: Result}},zh: {step: {stepDesc1: 掃描…

數據可視化 - 單子圖

一、認識單子圖 import matplotlib.pyplot as plt import numpy as np import pandas as pdplt.figure(num單子圖, figsize(12, 8), facecolorw) # 中文字體 plt.rcParams[font.sans-serif] KaiTi # 負號顯示 plt.rcParams[axes.unicode_minus] False# 2行&#xff0c;1列&a…

服務器上設置了代理之后,服務器可以訪問外網,但是不能訪問服務器本地。如何解決

你在服務器上設置了代理后&#xff0c;發現&#xff1a; 可以訪問外網不能訪問服務器本地地址&#xff08;如 localhost、127.0.0.1、內網IP&#xff09; 這是代理設置中常見的問題&#xff0c;尤其是當你設置了全局 HTTP/HTTPS 代理時。本地訪問也會被強制走代理&#xff0c…

mysql啟動報錯:Can‘t connect to local MySQL server through socket

文章目錄 一、報錯內容二、解決方法 一、報錯內容 在linux上啟動mysql時報錯 [rootlocalhost bin]# ./mysql -u root -p Enter password: ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock (2)執行以上命令后報錯&#xff0c;并且也…

C# Avalonia 綁定模式 Mode 的區別,它們的應用場景

C# Avalonia 綁定模式 Mode 的區別&#xff0c;它們的應用場景 文章目錄 1. **Default&#xff08;默認模式&#xff09;**2. **OneTime&#xff08;一次性綁定&#xff09;**3. **OneWay&#xff08;單向綁定&#xff09;**4. **TwoWay&#xff08;雙向綁定&#xff09;**5. *…

【OpenGL學習】(七)紋理單元

【OpenGL學習】&#xff08;七&#xff09;紋理單元 OpenGL的紋理單元&#xff08;Texture Unit&#xff09;是GPU中用于管理和組織紋理資源的邏輯單元&#xff0c;它允許開發者在渲染過程中同時使用多個紋理&#xff0c;并通過采樣器&#xff08;Sampler&#xff09;在著色器…