1. 模块概述

1.1 Acceptor
Acceptor 是 mymuduo 网络库中负责接受新连接的组件,它封装了监听 socket 的创建、绑定、监听和接受连接等操作。Acceptor 的核心设计目标包括:
- 监听 socket 管理:创建和管理监听 socket
- 新连接接受:接受客户端连接并获取对端地址
- 回调通知:通过回调机制通知上层有新连接到达
- Socket 选项配置:支持地址重用、端口重用等选项
cpp
+--------------------------------------------------+
| 主线程 Acceptor |
+--------------------------------------------------+
| |
| +-------------+ +-------------+ |
| | Socket | | Channel | |
| | (listen fd) | | (监听读事件) | |
| +-------------+ +-------------+ |
| |
| +------------------------------------------+ |
| | NewConnectionCallback | |
| | void(int sockfd, const InetAddress&) | |
| +------------------------------------------+ |
| |
+--------------------------------------------------+
| 组件 | 角色 | 线程归属 | 职责 |
|---|---|---|---|
| Acceptor | MainReactor | 主线程(单个) | 1. 监听端口 2. accept() 接收新连接 3. 分发连接给 SubReactor |
| EventLoopThreadPool | SubReactor 池 | 工作线程(多个) | 1. 处理已建立连接的 I/O 事件 2. 执行业务逻辑 |
1.2 TcpConnection
TcpConnection 是 mymuduo 网络库中代表 TCP 连接的核心类,它封装了一个已建立的 TCP 连接的所有状态和操作。TcpConnection 的核心设计目标包括:
- 连接状态管理:管理连接的完整生命周期状态
- 数据收发:提供线程安全的数据发送接口
- 事件处理:处理 socket 的读写关闭错误事件
- 回调机制:为用户提供丰富的事件回调接口
- 生命周期管理:通过 shared_ptr 实现安全的对象生命周期
1.3 TcpServer
TcpServer 是 mymuduo 网络库的核心服务器类,它封装了 TCP 服务器的完整生命周期管理。TcpServer 的核心设计目标包括:
-
连接管理:管理所有客户端连接的创建、维护和销毁
-
线程池管理:支持多线程处理,实现 Reactor 线程池模式
-
回调管理:提供丰富的回调接口供用户自定义业务逻辑
-
资源管理:自动管理服务器资源,确保正确释放
+--------------------------------------------------+
| TcpServer |
+--------------------------------------------------+
| |
| +-------------+ +------------------------+ |
| | Acceptor | | EventLoopThreadPool | |
| | (mainLoop) | | (subLoops) | |
| +------+------+ +-----------+------------+ |
| | | |
| v +-----------+-----------+ |
| 新连接到达 | | | | | |
| | v v v v | |
| | Loop0 Loop1 Loop2 Loop3 | |
| | | | | | | |
| +---------->+-----+-----+-----+-----+ |
| 分发新连接到 subLoop |
| |
+--------------------------------------------------+
2. 源码
2.1 Acceptor.h
cpp
#pragma once
#include "NonCopyable.h" // 不可拷贝基类
#include "Socket.h" // Socket类
#include "Channel.h" // Channel类
#include <functional> // 函数对象
namespace mymuduo {
class EventLoop;
class InetAddress;
/**
*
* ================================================
* ** 外部模块调用关系 **
* ================================================
*
* | 调用方模块 | 调用的Acceptor方法 | 用途 |
* |---------------------|-------------------------------|----------------------------------|
* | TcpServer | Acceptor() 构造函数 | 创建监听器 |
* | TcpServer | setNewConnectionCallback() | 设置新连接回调 |
* | TcpServer | listen() | 启动监听 |
* | EventLoop | handleRead() (内部事件触发) | 处理新连接事件 |
*
* ================================================
*/
class Acceptor : NonCopyable // 继承自不可拷贝基类
{
public:
/**
* @brief 新连接到达时的回调函数类型
* @param sockfd 新连接的socket文件描述符
* @param addr 客户端地址信息
*/
using NewConnectionCallback = std::function<void(int sockfd, const InetAddress&)>;
/**
* @brief 构造函数
* @param loop 所属的事件循环
* @param listenAddr 要监听的地址
* @param reuseport 是否启用端口重用
*/
Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport);
~Acceptor(); // 析构函数
/**
* @brief 设置新连接回调函数
* @param cb 回调函数对象
*/
void setNewConnectionCallback(const NewConnectionCallback &cb) {
NewConnectionCallback_ = std::move(cb);
}
/**
* @brief 获取当前是否正在监听
* @return true表示正在监听,false表示未监听
*/
bool listenning() const {return listenning_;}
void listen(); // 开始监听连接
private:
void handleRead(); //处理可读事件(新连接到达)
EventLoop *loop_; // 所属的事件循环
Socket acceptSocket_; // 监听socket
Channel acceptChannel_; // 监听socket对应的channel
NewConnectionCallback NewConnectionCallback_; // 新连接回调函数,根据轮询选择一个subloop唤醒,把当前connfd封装成channel发给它
bool listenning_; // 是否正在监听的标志
};
} // namespace mymuduo
2.2 Acceptor.cpp
cpp
#include "Acceptor.h"
#include "LogStream.h"
#include "InetAddress.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
using namespace mymuduo;
/**
* @brief 创建非阻塞的socket文件描述符
* @return 成功返回socket文件描述符,失败退出程序
*/
static int createNonblocking() {
// 创建非阻塞且close-on-exec的TCP socket
int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
if (sockfd < 0) {
LOG_ERROR << "listen socket create err: " << errno;
exit(-1);
}
return sockfd;
}
/**
* @brief 构造函数
* @param loop 所属的事件循环
* @param listenAddr 监听的地址
* @param reuseport 是否启用端口重用
*/
Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport)
: loop_(loop) // 所属事件循环
, acceptSocket_(createNonblocking()) // 创建监听socket
, acceptChannel_(loop, acceptSocket_.fd()) // 把socketfd打包为一个channel给mianloop的poller
, listenning_(false) // 初始状态未开始监听
{
acceptSocket_.setReuseAddr(true); // 启用地址重用
acceptSocket_.setReusePort(true); // 启用端口重用
acceptSocket_.bindAddress(listenAddr); // 绑定监听地址
// 设置channel的读事件回调,只关心读事件,即新连接
acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));
}
Acceptor::~Acceptor() {
// 禁用所有事件并从事件循环中移除channel
acceptChannel_.disableAll();
acceptChannel_.remove();
}
/**
* @brief 开始监听连接
*/
void Acceptor::listen() {
listenning_ = true; // 标记为正在监听状态
acceptSocket_.listen(); // 开始监听socket连接
acceptChannel_.enableReading();// 启用channel的读事件监听
}
/**
* @brief 处理新连接到达的读事件
*/
void Acceptor::handleRead() {
InetAddress peerAddr; // 用于保存客户端地址
// 接受新连接
int connfd = acceptSocket_.accept(&peerAddr);
if (connfd >= 0) {
// 如果有新连接回调函数,则调用
if (NewConnectionCallback_) {
NewConnectionCallback_(connfd, peerAddr);
} else {
// 否则直接关闭连接
::close(connfd);
}
} else {
LOG_ERROR << "accept err: " << errno;
// 特殊处理文件描述符耗尽的情况
if (errno == EMFILE) LOG_ERROR << "sockfd reached limit";
}
}
2.3 TcpConnection.h
cpp
#pragma once
#include "NonCopyable.h" // 禁止拷贝的基类
#include "InetAddress.h" // 网络地址封装
#include "Callbacks.h" // 回调函数定义
#include "Buffer.h" // 数据缓冲区
#include "Timestamp.h" // 时间戳
#include <memory>
#include <string>
#include <atomic>
namespace mymuduo {
class Channel; // 事件通道(封装fd和事件回调)
class EventLoop; // 事件循环
class Socket; // 套接字封装
/**
* ================================================
* ** 外部模块调用关系 **
* ================================================
*
* | 调用方模块 | 调用的TcpConnection方法 | 用途 |
* |---------------------|-------------------------------|----------------------------------|
* | TcpServer | connectEstablished() | 通知连接已建立 |
* | TcpServer | connectDestroyed() | 通知连接已销毁 |
* | 用户代码 | send()/sendFile() | 发送数据 |
* | 用户代码 | shutdown() | 关闭连接 |
* | 用户代码 | setXXXCallback()系列方法 | 设置各类事件回调 |
* | EventLoop | handleRead()/handleWrite()等 | 处理底层socket事件 |
*
* ================================================
*/
class TcpConnection : NonCopyable, // 禁止拷贝
public std::enable_shared_from_this<TcpConnection> { // 支持shared_from_this
public:
/**
* @brief 构造函数
* @param loop 所属事件循环(必须是IO线程)
* @param nameArg 连接名称(用于日志标识)
* @param sockfd 已连接的套接字
* @param localAddr 本地地址
* @param peerAddr 对端地址
*/
TcpConnection(EventLoop *loop,
const std::string &nameArg,
int sockfd,
const InetAddress &localAddr,
const InetAddress &peerAddr);
~TcpConnection() {};
// 获取基本信息
EventLoop* getLoop() const { return loop_; }
const std::string& name() const { return name_; }
const InetAddress& localAddress() const { return localAddr_; }
const InetAddress& peerAddress() const { return peerAddr_; }
// 连接状态检查
bool connected() const { return state_ == kConnected; }
// 数据发送接口
void send(const std::string &buf); // 发送字符串数据
void sendFile(int fileDescriptor, // 发送文件(零拷贝)
off_t offset,
size_t count);
// 关闭连接(半关闭)
void shutdown();
// 设置各类回调函数
void setConnectionCallback(const ConnectionCallback &cb)
{ connectionCallback_ = cb; }
void setMessageCallback(const MessageCallback &cb)
{ messageCallback_ = cb; }
void setWriteCompleteCallback(const WriteCompleteCallback &cb)
{ writeCompleteCallback_ = cb; }
void setCloseCallback(const CloseCallback &cb)
{ closeCallback_ = cb; }
void setHighWaterMarkCallback(const HighWaterMarkCallback &cb, size_t highWaterMark)
{ highWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark; }
// 连接管理
void connectEstablished(); // 在IO线程中调用,通知连接已建立
void connectDestroyed(); // 在IO线程中调用,通知连接已销毁
private:
// 连接状态枚举
enum StateE {
kDisconnected, // 已断开
kConnecting, // 正在连接(未使用)
kConnected, // 已连接
kDisconnecting // 正在断开
};
void setState(StateE state) { state_ = state; }
// Channel事件回调
void handleRead(Timestamp receiveTime); // 处理可读事件
void handleWrite(); // 处理可写事件
void handleClose(); // 处理关闭事件
void handleError(); // 处理错误事件
// 线程安全的内部实现
void sendInLoop(const void *data, size_t len);
void shutdownInLoop();
void sendFileInLoop(int fileDescriptor, off_t offset, size_t count);
private:
EventLoop *loop_; // 所属事件循环(必须是IO线程)
const std::string name_; // 连接名称(用于日志和调试)
std::atomic_int state_; // 连接状态(原子操作,线程安全)
bool reading_; // 是否正在监听可读事件
// 核心组件
std::unique_ptr<Socket> socket_; // 套接字对象(RAII管理fd)
std::unique_ptr<Channel> channel_; // 事件通道
// 地址信息
const InetAddress localAddr_; // 本地地址
const InetAddress peerAddr_; // 对端地址
// 用户回调函数
ConnectionCallback connectionCallback_; // 连接建立/关闭回调
MessageCallback messageCallback_; // 消息到达回调
WriteCompleteCallback writeCompleteCallback_; // 数据发送完成回调
HighWaterMarkCallback highWaterMarkCallback_; // 高水位回调(流量控制)
CloseCallback closeCallback_; // 连接关闭回调
size_t highWaterMark_; // 高水位阈值
// 数据缓冲区
Buffer inputBuffer_; // 输入缓冲区(存放接收的数据)
Buffer outputBuffer_; // 输出缓冲区(存放待发送的数据)
};
} // namespace mymuduo
2.4 TcpConnection.cpp
cpp
#include "TcpConnection.h"
#include "Socket.h"
#include "Channel.h"
#include "EventLoop.h"
#include "LogStream.h"
#include <sys/sendfile.h> // 零拷贝发送文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace mymuduo;
// 辅助函数:检查EventLoop是否有效
static EventLoop* CheckLoopNotNull(EventLoop* loop) {
if (loop == nullptr) {
LOG_ERROR << "mainLoop is null!!!";
exit(-1);
}
return loop;
}
/**
* @brief TcpConnection构造函数
* @param loop 所属事件循环(必须非空)
* @param nameArg 连接名称标识
* @param sockfd 已建立的TCP套接字
* @param localAddr 本地地址信息
* @param peerAddr 对端地址信息
*/
TcpConnection::TcpConnection(EventLoop* loop,
const std::string& nameArg,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr)
: loop_(CheckLoopNotNull(loop)), // 校验loop有效性
name_(nameArg),
state_(kConnecting), // 初始状态为连接中
reading_(true), // 默认开启读监听
socket_(new Socket(sockfd)), // 接管套接字所有权
channel_(new Channel(loop, sockfd)), // 创建事件通道
localAddr_(localAddr),
peerAddr_(peerAddr),
highWaterMark_(64 * 1024 * 1024) { // 默认高水位64MB
// 设置Channel事件回调(绑定到当前对象成员函数)
channel_->setReadCallback(std::bind(&TcpConnection::handleRead, this, std::placeholders::_1));
channel_->setWriteCallback(std::bind(&TcpConnection::handleWrite, this));
channel_->setCloseCallback(std::bind(&TcpConnection::handleClose, this));
channel_->setErrorCallback(std::bind(&TcpConnection::handleError, this));
LOG_INFO << "TcpConnection::ctor[" << name_ << "] at fd=" << sockfd;
socket_->setKeepAlive(true); // 开启TCP保活机制
}
/********************* 数据发送相关方法 *********************/
/**
* @brief 发送字符串数据(线程安全)
* @param buf 待发送数据
* 原理:跨线程调用通过runInLoop转发到IO线程执行
*/
void TcpConnection::send(const std::string& buf) {
if (state_ == kConnected) { // 仅连接状态下允许发送
if (loop_->isInLoopThread()) {
sendInLoop(buf.c_str(), buf.size()); // 直接执行
} else {
// 跨线程时通过事件队列转发
loop_->runInLoop( std::bind(&TcpConnection::sendInLoop, this, buf.c_str(), buf.size()));
}
}
}
/**
* @brief 在IO线程中执行实际发送操作
* @param data 数据指针
* @param len 数据长度
* 策略:优先尝试直接发送,失败则存入输出缓冲区并监听可写事件
*/
void TcpConnection::sendInLoop(const void* data, size_t len) {
ssize_t nwrote = 0;
size_t remaining = len;
bool faultError = false;
// 状态检查
if (state_ == kDisconnected) {
LOG_ERROR << "disconnected, give up writing";
return;
}
// Case1: 当前无待发送数据且通道未注册写事件
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0) {
nwrote = ::write(channel_->fd(), data, len); // 尝试直接发送
if (nwrote >= 0) {
remaining = len - nwrote;
// 全部发送完成且设置了写完成回调
if (remaining == 0 && writeCompleteCallback_) {
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
} else { // 发送出错处理
nwrote = 0;
if (errno != EWOULDBLOCK) { // 非预期错误
LOG_ERROR << "TcpConnection::sendInLoop";
if (errno == EPIPE || errno == ECONNRESET) { // 连接异常
faultError = true;
}
}
}
}
// Case2: 仍有剩余数据待发送或发生非致命错误
if (!faultError && remaining > 0) {
size_t oldLen = outputBuffer_.readableBytes();
// 高水位检测(流量控制)
if (oldLen + remaining >= highWaterMark_ && oldLen < highWaterMark_ && highWaterMarkCallback_) {
loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
}
// 剩余数据存入输出缓冲区
outputBuffer_.append(static_cast<const char*>(data) + nwrote, remaining);
if (!channel_->isWriting()) {
channel_->enableWriting(); // 注册可写事件监听
}
}
}
/********************* 连接管理方法 *********************/
/**
* @brief 关闭连接(半关闭,线程安全)
* 原理:通过shutdown关闭写端,保留读端直到对端关闭
*/
void TcpConnection::shutdown() {
if (state_ == kConnected) {
setState(kDisconnecting); // 状态转移
loop_->runInLoop( // 确保在IO线程执行
std::bind(&TcpConnection::shutdownInLoop, this));
}
}
/**
* @brief 实际执行关闭操作(必须在IO线程)
* 条件:输出缓冲区数据已全部发送完成
*/
void TcpConnection::shutdownInLoop() {
if (!channel_->isWriting()) { // 确保数据已发送完毕
socket_->shutdownWrite(); // 关闭写端
}
}
/********************* 连接生命周期方法 *********************/
/**
* @brief 通知连接已建立(由TcpServer调用)
* 流程:1. 设置状态 2. 绑定shared_ptr 3. 注册读事件 4. 回调用户
*/
void TcpConnection::connectEstablished() {
setState(kConnected);
channel_->tie(shared_from_this()); // 防止Channel回调时对象被销毁
channel_->enableReading(); // 注册读事件监听
// 新连接建立,执行回调
connectionCallback_(shared_from_this());
}
/**
* @brief 通知连接已销毁(由TcpServer调用)
* 流程:1. 取消所有事件监听 2. 触发回调 3. 从Poller移除Channel
*/
void TcpConnection::connectDestroyed() {
if (state_ == kConnected) {
setState(kDisconnected);
channel_->disableAll(); // 取消所有事件监听
connectionCallback_(shared_from_this());
}
channel_->remove(); // 从Poller中移除
}
/********************* 事件回调处理 *********************/
/**
* @brief 处理可读事件
* @param receiveTime 数据到达时间戳
* 流程:1. 读取数据到输入缓冲区 2. 触发消息回调
*/
void TcpConnection::handleRead(Timestamp receiveTime) {
int savedErrno = 0;
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
if (n > 0) {
// 触发用户消息到达回调(数据存放在inputBuffer_)
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
} else if (n == 0) { // 对端关闭连接
handleClose();
} else { // 读取错误
errno = savedErrno;
LOG_ERROR << "TcpConnection::handleRead";
handleError();
}
}
/**
* @brief 处理可写事件
* 流程:1. 发送输出缓冲区数据 2. 根据发送情况调整监听状态
*/
void TcpConnection::handleWrite() {
if (channel_->isWriting()) {
int savedErrno = 0;
ssize_t n = outputBuffer_.writeFd(channel_->fd(), &savedErrno);
if (n > 0) {
outputBuffer_.retrieve(n); // 移动读指针
// 缓冲区数据已全部发送完成
if (outputBuffer_.readableBytes() == 0) {
channel_->disableWriting(); // 取消写事件监听
if (writeCompleteCallback_) {
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
// 如果正在关闭连接,执行最终关闭
if (state_ == kDisconnecting) shutdownInLoop();
}
} else {
LOG_ERROR << "TcpConnection::handleWrite";
}
} else {
LOG_ERROR << "Connection fd = " << channel_->fd() << " is down, no more writing";
}
}
/**
* @brief 处理连接关闭
* 流程:1. 更新状态 2. 取消事件监听 3. 触发用户回调
*/
void TcpConnection::handleClose() {
LOG_INFO << "fd = " << channel_->fd() << " state = " << state_;
setState(kDisconnected);
channel_->disableAll(); // 取消所有事件监听
TcpConnectionPtr connPtr(shared_from_this());
connectionCallback_(connPtr); // 连接状态变更回调
closeCallback_(connPtr); // 关闭回调(最终会触发TcpServer移除连接)
}
/**
* @brief 处理错误事件
* 流程:获取套接字错误码并记录日志
*/
void TcpConnection::handleError() {
int optval;
socklen_t optlen = sizeof(optval);
int err = 0;
if (::getsockopt(channel_->fd(), SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
err = errno;
} else {
err = optval;
}
LOG_ERROR << "TcpConnection::handleError [" << name_
<< "] - SO_ERROR = " << err;
}
/********************* 零拷贝文件发送 *********************/
/**
* @brief 发送文件(线程安全)
* @param fileDescriptor 文件描述符
* @param offset 起始偏移量
* @param count 发送字节数
* 原理:使用sendfile系统调用实现内核级零拷贝
*/
void TcpConnection::sendFile(int fileDescriptor, off_t offset, size_t count) {
if (connected()) {
if (loop_->isInLoopThread()) {
sendFileInLoop(fileDescriptor, offset, count);
} else {
loop_->runInLoop(
std::bind(&TcpConnection::sendFileInLoop,
shared_from_this(),
fileDescriptor,
offset,
count));
}
} else {
LOG_ERROR << "TcpConnection::sendFile - not connected";
}
}
/**
* @brief 实际执行文件发送(必须在IO线程)
* 策略:优先使用sendfile,失败则转入异步重试
*/
void TcpConnection::sendFileInLoop(int fileDescriptor, off_t offset, size_t count) {
ssize_t bytesSent = 0;
size_t remaining = count;
bool faultError = false;
if (state_ == kDisconnecting) {
LOG_ERROR << "disconnected, give up writing";
return;
}
// Case1: 当前无待发送数据
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0) {
bytesSent = ::sendfile(socket_->fd(), fileDescriptor, &offset, remaining);
if (bytesSent >= 0) {
remaining -= bytesSent;
if (remaining == 0 && writeCompleteCallback_) {
loop_->queueInLoop(
std::bind(writeCompleteCallback_, shared_from_this()));
}
} else {
if (errno != EWOULDBLOCK) {
LOG_ERROR << "TcpConnection::sendFileInLoop";
if (errno == EPIPE || errno == ECONNRESET) {
faultError = true;
}
}
}
}
// Case2: 处理剩余数据或错误
if (!faultError && remaining > 0) {
loop_->queueInLoop(
std::bind(&TcpConnection::sendFileInLoop,
shared_from_this(),
fileDescriptor,
offset,
remaining));
}
}
2.5 TcpServer.h
cpp
#pragma once
#include "EventLoop.h" // 事件循环类
#include "Acceptor.h" // 接收器类,处理新连接
#include "InetAddress.h" // 网络地址封装
#include "NonCopyable.h" // 不可拷贝基类
#include "EventLoopThreadPool.h" // 事件循环线程池
#include "Callbacks.h" // 回调函数定义
#include "TcpConnection.h" // TCP连接类
#include "Buffer.h" // 缓冲区类
#include <functional> // 函数对象
#include <string> // 字符串
#include <memory> // 智能指针
#include <atomic> // 原子操作
#include <unordered_map> // 哈希表
namespace mymuduo {
/**
* ================================================
* ** 外部模块调用关系 **
* ================================================
*
* | 调用方模块 | 调用的TcpServer方法 | 用途 |
* |---------------------|----------------------------------|----------------------------------|
* | 主程序 | TcpServer() 构造函数 | 创建服务器实例 |
* | 主程序 | setThreadNum() | 设置工作线程数量 |
* | 主程序 | setXXXCallback() 系列方法 | 设置各类事件回调 |
* | 主程序 | start() | 启动服务器 |
* | Acceptor | newConnection() (内部调用) | 处理新连接 |
* | TcpConnection | removeConnection() (内部调用) | 移除断开连接 |
* | EventLoopThreadPool | threadInitCallback_ (回调) | 线程池初始化时执行自定义逻辑 |
*
* ================================================
*/
class TcpServer : NonCopyable // 继承自不可拷贝基类
{
public:
using ThreadInitCallback = std::function<void(EventLoop*)>; // 线程初始化回调类型
/**
* @brief 端口重用选项枚举
*/
enum Option
{
kNoReusePort, // 不重用端口
kReusePort, // 重用端口(SO_REUSEPORT)
};
/**
* @brief 构造函数
* @param loop 事件循环指针
* @param listenAddr 监听地址
* @param nameArg 服务器名称
* @param option 端口重用选项,默认为不重用
*/
TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const std::string& nameArg,
Option option = kNoReusePort);
~TcpServer(); // 析构函数
// 设置各种回调函数
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; } // 设置写完成回调
/**
* @brief 设置工作线程数量
* @param numThreads 线程数量
*
* @note 必须在start()前调用
*/
void setThreadNum(int numThreads);
void start(); // 启动服务器
private:
using ConnectionMap = std::unordered_map<std::string, TcpConnectionPtr>; // 连接映射表类型
/**
* @brief 新连接到达时的处理函数
* @param sockfd 新连接的socket文件描述符
* @param peerAddr 对端地址
*/
void newConnection(int sockfd, const InetAddress& peerAddr);
/**
* @brief 移除连接
* @param conn 要移除的连接指针
*/
void removeConnection(const TcpConnectionPtr& conn);
/**
* @brief 在事件循环中移除连接
* @param conn 要移除的连接指针
*/
void removeConnectionInLoop(const TcpConnectionPtr& conn);
EventLoop* loop_; // 主事件循环指针
const std::string ipPort_; // 服务器监听的IP和端口字符串表示
const std::string name_; // 服务器名称
std::unique_ptr<Acceptor> acceptor_; // 接收器,负责接受新连接
std::shared_ptr<EventLoopThreadPool> threadPool_; // 事件循环线程池
// 各种回调函数
ConnectionCallback connectionCallback_; // 连接建立/关闭回调
MessageCallback messageCallback_; // 消息到达回调
WriteCompleteCallback writeCompleteCallback_; // 写完成回调
ThreadInitCallback threadInitCallback_; // 线程初始化回调
int numThreads_; // 线程数量
std::atomic_int started_; // 服务器启动状态(原子变量)
int nextConnId_; // 下一个连接ID
ConnectionMap connections_; // 当前所有连接的映射表
};
} // namespace mymuduo
2.6 TcpServer.cpp
cpp
#include "TcpServer.h"
#include "LogStream.h"
#include "TcpConnection.h"
#include <functional>
#include <string.h>
using namespace mymuduo;
/**
* @brief 检查事件循环指针是否有效
* @param loop 要检查的事件循环指针
* @return 有效的事件循环指针
*
* @note 如果传入的指针为空,会记录错误日志并退出程序
*/
static EventLoop* CheckLoopNotNull(EventLoop* loop) {
if (loop == nullptr) {
LOG_ERROR << "mainLoop is null!!!";
exit(-1);
}
return loop;
}
/**
* @brief TcpServer构造函数
* @param loop 主事件循环
* @param listenAddr 服务器监听地址
* @param nameArg 服务器名称
* @param option 端口重用选项
*
* 初始化服务器各个组件,设置Acceptor的新连接回调
*/
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_()
, writeCompleteCallback_()
, threadInitCallback_()
, nextConnId_(1)
, started_(0)
{
// 设置Acceptor的新连接回调,有新连接时调用newConnection处理
acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2));
}
/**
* @brief TcpServer析构函数
*
* 清理所有连接,确保资源正确释放
*/
TcpServer::~TcpServer() {
for (auto& item : connections_) {
TcpConnectionPtr conn(item.second);
item.second.reset();
conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}
}
/**
* @brief 设置线程池中的线程数量
* @param numThreads 要设置的工作线程数量
*
* @note 必须在start()之前调用
*/
void TcpServer::setThreadNum(int numThreads) {
threadPool_->setThreadNum(numThreads);
}
/**
* @brief 启动服务器
*
* 启动线程池并开始监听连接
*/
void TcpServer::start() {
if (started_.fetch_add(1) == 0) { // 获得旧值并旧值加1
// 启动线程池
threadPool_->start(threadInitCallback_);
// 在主事件循环中开始监听
loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));
}
}
/**
* @brief 处理新连接
* @param sockfd 新连接的socket描述符
* @param peerAddr 对端地址
*
* 为新连接创建TcpConnection对象,设置各种回调并加入到连接管理
*/
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr) {
// 从线程池获取一个事件循环
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 [" << name_.c_str()
<< "] - new connection [" << connName.c_str()
<< "] from " << peerAddr.toIpPort().c_str();
// 获取本地地址
sockaddr_in local;
::memset(&local, 0, sizeof(local));
socklen_t addrlen = sizeof(local);
if (::getsockname(sockfd, (sockaddr*)&local, &addrlen) < 0) {
LOG_ERROR << "sockets::getLocalAddr";
}
InetAddress localAddr(local);
// 创建新的TcpConnection对象
TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr));
// 将连接加入管理
connections_[connName] = conn;
// 设置各种回调
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
conn->setWriteCompleteCallback(writeCompleteCallback_);
conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
// 在IO线程中建立连接
ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}
/**
* @brief 移除连接
* @param conn 要移除的连接
*
* 将连接移除操作调度到主事件循环中执行
*/
void TcpServer::removeConnection(const TcpConnectionPtr& conn) {
loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
/**
* @brief 在事件循环中移除连接
* @param conn 要移除的连接
*
* 实际执行连接移除操作,并从连接管理中删除
*/
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn) {
LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_.c_str() << "] - connection " << conn->name().c_str();
// 从连接管理中移除
connections_.erase(conn->name());
// 在IO线程中销毁连接
EventLoop* ioLoop = conn->getLoop();
ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}
3. 模块交互
EventLoop 通过 Poller(epoll) 同时监听三类 fd :
1️⃣ wakeupFd_ (所有 EventLoop 必备)→ 唤醒阻塞的 poll
2️⃣ listenfd (仅 MainReactor)→ 接收新连接
3️⃣ 多个 connfd(SubReactor)→ 处理业务数据