muduo网络库
总体架构
Muduo 主要由三大部分组成:
- 基础工具模块(Base)
Logger、Timestamp、Thread、ThreadPool等提供线程安全、时间戳、日志等基础设施
- 网络模块(Net)
核心类:EventLoop、Channel、Poller、Socket、Acceptor、TcpServer、TcpConnection、Connector、Buffer基于 Reactor 模型,用 epoll 实现 I/O 复用
- 应用层接口
事件回调(connectionCallback / messageCallback / writeCompleteCallback)与 Muduo 交互的
核心运行机制 — 主从Reactor 模型
|
三大核心组建
EventLoop
EventLoop 是 一条线程内的事件调度器:负责阻塞等待内核 I/O 事件(通过 Epoller),把就绪的 Channel 分发处理,并按需执行定时任务与跨线程提交的回调。
loop(int timeout = -1)
|
quit()
|
runInLoop(Functor cb)
|
queueInLoop(Functor cb)
|
Epoller
Epoller 是对 Linux epoll 的封装,它把操作系统的 I/O 多路复用(epoll_create1/epoll_ctl/epoll_wait)封装成更易用、面向对象的接口。它的职责包括:
|
poll(timeoutMs, activeChannels)
调用 int n = ::epoll_wait(epollfd_, events_.data(), events_.size(), timeoutMs);
返回值通常还会封装一个 Timestamp(表示 epoll_wait 返回的时间),传给上层 EventLoop。
fillActiveChannels(numEvents, activeChannels)
把events_[i]
中的 data.ptr
(或 data.fd)转换为 Channel*
,设置 Channel 的 revents(就绪事件位),并 push_back
到 activeChannels
;
updateChannel(Channel*)
- 把 Channel 的期望事件注册/修改到 epoll。
- 维护 Channel 的状态(new/added),用来决定 ADD / MOD。
- 若 epoll_ctl 失败,要根据 errno 做合理处理并记录日志(例如 EEXIST 表明意外重复 add)。
removeChannel(Channel*)
从 epoll 中删除 fd(EPOLL_CTL_DEL);
epoll_event.events 常见标志(以及 Muduo 常用)
|
Channel
- Channel 封装 一个 fd + 它关心的事件掩码 + 这些事件发生时要调用的回调。
- 它不持有 fd 的生命周期(不 close fd),只是作为 EventLoop ⇄ Poller ⇄ 业务回调 的桥梁。
handleEvent — 事件真正被执行的地方
判断处理的类型,并执行对应的回调函数
线程安全与归属
-
Channel 属于某个 EventLoop(创建时记录 loop_),其大部分方法必须在该 loop 线程调用(assertInLoopThread())。
-
如果要从其他线程修改 Channel 的事件,应该通过
EventLoop::runInLoop/queueInLoop
让 loop 线程执行修改(以确保线程安全)。
muduo业务层
TcpConnection
- 表示一个 TCP 连接(客户端连接到服务器,或者客户端连接到远程服务器)
- 管理该连接的状态(连接中、已连接、断开等)
- 封装读写操作和缓冲区管理,实现高效的非阻塞 IO
- 绑定一个 Channel 来监听该连接的读写事件
- 保存各种业务回调,当连接建立、关闭、读写事件时通知业务层
- 保证线程安全,大部分操作在 EventLoop 线程里执行
构造函数
里面设置了各种回调,去处理读写事件,关闭连接,和错误
send(message)
- 先尝试直接写入 socket(如果缓冲区为空),写不完的数据放入 outputBuffer_。
- 打开
Channel
的写事件监听(enableWriting()),等待 epoll 告诉你可写后继续写。 - 发送完成后调用
writeCompleteCallback_
,通知业务层数据已经全部发出。
TcpServer
构造函数
初始化主循环、Acceptor 监听指定地址端口
默认不启用端口复用,可以选用 kReusePort
关联新连接回调,准备接收连接
start()
启动服务器:
|
这个函数只允许调用一次
newConnection(int sockfd, const InetAddress& peerAddr)
-
新连接建立时被调用(由
Acceptor
触发) -
生成唯一连接名字
-
选择一个
IO EventLoop
(通过线程池轮询) -
创建新的
TcpConnection
对象,绑定对应 EventLoop 和 socket fd -
绑定
TcpConnection
的各种回调(连接、消息、关闭、写完成等) -
把连接加入
connections_
容器管理 -
调用
TcpConnection::connectEstablished()
通知连接建立
removeConnection(const TcpConnectionPtr& conn)
-
连接关闭时被调用
-
从
connections_
容器移除连接 -
调用
TcpConnection::connectDestroyed()
执行清理 -
这个函数会被
CloseCallback
触发
Muduo 的网络封装层
Acceptor
构造函数
|
Acceptor::listen()
调用 listen() 后,监听 socket 的 EPOLLIN 事件就会被 EventLoop 关注。
当有新连接到来,epoll_wait() 会返回监听 fd 可读,触发 handleRead()。
handleRead()
新连接建立后,去触发回调函数
Socket(套接字封装类)
|
InetAddress(网络地址类)
|
muduo基础工具
Buffer
- 封装了 TCP 收发数据的内存缓冲区
- 提供高效、连续的字节存储,避免手动 malloc/free 和拷贝
- 方便按需读取/写入,不用一次性全部处理
|
Timer / TimerQueue(定时器)
作用
-
在 EventLoop 中实现高效的定时任务管理
-
支持一次性任务和周期性任务
-
使用 Linux timerfd 机制与 epoll 集成
为什么要有 Timer?
在网络编程中,你可能需要:
-
定时关闭空闲连接(心跳超时)
-
定时发送心跳包
-
延迟执行某个任务
这些任务必须和 Reactor 的事件循环集成,否则就得自己写一套线程安全的定时器机制。
自己实现的muduo功能
├── base
│ ├── CurrentThread.cc
│ ├── CurrentThread.h
│ ├── logger.cc
│ ├── logger.h
│ ├── logStream.cc
│ ├── logStream.h
│ ├── noncopyable.h
│ ├── Timestamp.cc
│ └── Timestamp.h
├── CMakeLists.txt
└── net
├── Acceptor.cc
├── Acceptor.h
├── Buffer.cc
├── Buffer.h
├── Channel.cc
├── Channel.h
├── Connector.cc
├── Connector.h
├── Epoller.cc
├── Epoller.h
├── EventLoop.cc
├── EventLoop.h
├── EventLoopThread.h
├── EventLoopThreadpool.cc
├── EventLoopThreadpool.h
├── InetAddress.h
├── Poller.h
├── sigpipe.h
├── Socket.h
├── SocketOps.cc
├── SocketOps.h
├── TcpClient.cc
├── TcpClient.h
├── TcpConnection.cc
├── TcpConnection.h
├── TcpServer.cc
├── TcpServer.h
├── Timer.cc
├── Timer.h
├── TimerId.h
├── TimerQueue.cc
└── TimerQueue.h