在高性能网络编程领域,muduo 库无疑是 Linux 下 Reactor 模型的经典实现,其优雅的设计和极致的性能深受开发者推崇。本文将带大家从零复刻 muduo 核心架构,基于 Reactor 模型实现一个轻量级、高性能的网络通信框架,深入剖析 Reactor 模型的核心原理,以及 muduo 库的关键设计思路。
一、Reactor 模型核心原理
1.Reactor 模型
Reactor 模型(反应器模式)是高性能 IO 框架的基石,其核心是 "事件驱动",核心流程可概括为:
(1)事件注册:将套接字(fd)及其关注的事件(读 / 写 / 异常)注册到 IO 多路复用器(epoll/kqueue/select);
(2)事件监听:IO 多路复用器阻塞等待事件触发;
(3)事件分发:当事件触发时,多路复用器返回就绪事件,由 Reactor 将事件分发给对应的事件处理器;
(4)事件处理:事件处理器完成具体的业务逻辑(如读数据、写数据、连接处理)。
muduo 库基于单 Reactor + 多线程架构(主 Reactor 负责监听连接事件,子 Reactor 负责处理 IO 事件),结合 epoll ET(边缘触发)模式,最大化利用 Linux 内核的 IO 性能。

2.扩展:I/O编程模型
(1)基础I/O模型
①阻塞 IO (BIO) 调用 recv/accept 一直等数据,线程卡死,一连接一线程,并发极低。
②非阻塞 IO (NIO) 无数据直接返回,不停轮询,CPU 空转浪费大。
③IO 多路复用 select/poll/epoll/kqueue,单线程监听海量 fd,Reactor 底层就是它。
④信号驱动 IO 内核就绪发信号通知进程,极少使用。
⑤异步 IO (AIO) 内核完成读写后主动回调,真正全异步。
(2)高级网络模型
①Reactor 反应器(最常用):同步非阻塞事件驱动
②Proactor 预分配器:纯异步 IO 模型
区别 Reactor: Reactor:用户线程等事件就绪,自己读写 Proactor:内核完成读写,直接回调业务
口诀:Reactor:你来活了,快来读取数据;Proactor:数据我已经读完了,直接处理业务
③线程池模型(半同步半异步):主线程 accept 接连接,丢给固定线程池处理读写
④进程池模型:Nginx 经典模型:多进程 + 单进程 Reactor
⑤Worker-Worker 工作窃取模型:多工作线程互相偷取空闲任务,均衡负载
⑥流水线模型 (Pipeline):数据按阶段流转:接收→解码→业务→编码→发送,分层解耦
二、框架整体架构设计
1.框架
复刻的 muduo 框架核心模块划分与原库保持一致,整体架构如下:
├── base/ // 基础工具类(日志、原子操作、非拷贝类等)
├── net/ // 核心网络模块
│ ├── EventLoop.h/cpp // 事件循环(Reactor核心)
│ ├── EpollPoller.h/cpp// epoll多路复用器实现
│ ├── Channel.h/cpp // 事件通道(封装fd和事件)
│ ├── Socket.h/cpp // 套接字封装
│ ├── Acceptor.h/cpp // 连接监听处理器
│ ├── TCPConnection.h/cpp // 连接封装
│ └── TCPServer.h/cpp // TCP服务器核心
└── main.cpp // 测试示例
2.核心模块职责
| 模块 | 核心职责 |
|---|---|
| EventLoop | 事件循环核心,负责运行 epoll_wait、分发事件、管理 Channel |
| EpollPoller | 封装 epoll 的创建、事件注册 / 修改 / 删除、等待就绪事件 |
| Channel | 封装 fd 和其关注的事件(读 / 写),绑定事件回调,由 EventLoop 分发事件时触发 |
| Acceptor | 监听 listenfd 的读事件,处理新连接,创建 TcpConnection |
| TCPServer | 封装服务器启动、端口监听、新连接回调管理 |
| TCPConnection | 封装已建立的连接,处理读 / 写事件,管理连接生命周期 |
3.整个网络库的全貌(抽象记忆)
整个网络库 = 一个【邮局 + 快递系统】
(1)EventLoop = 快递总站(事件循环)
(2)Channel = 快递员(负责一个文件描述符)
(3)Poller = 监控中心(epoll,等待事件)
(4)Buffer = 包裹 / 快递袋(数据缓冲区)
(5)TcpConnection = 一个客户(连接)
(6)Acceptor = 迎宾(负责接新客户)
(7)TcpServer = 老板(总控)
(8)TcpClient = 寄件人(客户端)
(9)Timer/Timestamp = 闹钟(定时任务)
(10)Socket = 快递通道(网络连接)
(11)Details = 工具包(日志、宏、辅助函数)
三、核心代码实现解析
1. details.h:日志宏
details.h
cpp
#include <cstdio>
#include <cstdlib>
//#include<cstring>
namespace net
{
#define LOG_DEBUG(fmt,...) printf("[%s:%d]" fmt"\n",__FILE__,__LINE__,##__VA_ARGS__)
#define LOG_ERROR(fmt,...) printf("[%s:%d]" fmt"\n",__FILE__,__LINE__,##__VA_ARGS__)
#define LOG_FATAL(fmt,...) \
{ \
printf("[%s:%d]" fmt"\n",__FILE__,__LINE__,##__VA_ARGS__); \
abort(); \
}
}
涉及知识点:
可变模板参:#define LOG_DEBUG(fmt,...)printf(...,#VA_ARGS)
2. Timestamp.h
Timestamp.h
cpp
#pragma once
#include <string>
#include <time.h>
#include <sys/time.h>
namespace net
{
class Timestamp
{
public:
Timestamp() : _microSecondsSinceEpoch1(0) {}
Timestamp(int64_t usec) : _microSecondsSinceEpoch1(usec) {}
// 1.判断时间戳是否有效:0就是无效的
bool valid() { return _microSecondsSinceEpoch1 > 0; }
// 2.获取时间戳对应的格式化字符串yyyy-mm-dd hh:mm:ss
std::string toFormatString()
{
// 将微秒单位的时间戳转换为秒的时间戳
time_t t = _microSecondsSinceEpoch1 / microSecondsPerSecond;
// 将时间戳转换成struct tm结构
struct tm lt;
localtime_r(&t, <);
// 组织子字符串
char buf[32];
snprintf(buf, 31, "%d-%d-%d %d:%d:%d",
lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday,
lt.tm_hour, lt.tm_min, lt.tm_sec);
return buf;
}
// 3.将unix中单位是秒的时间戳转为为微秒的时间戳
void fromUnixTime(time_t t)
{
return fromUnixTime(t, 0);
}
void fromUnixTime(time_t t, int usec)
{
_microSecondsSinceEpoch1 = t * microSecondsPerSecond + usec;
}
// 返回时间戳的值(微秒)
int64_t microSecondsSinceEpoch() const
{
return _microSecondsSinceEpoch1;
}
// 返回时间戳的值(秒)
time_t secondsSinceEpoch() const
{
return _microSecondsSinceEpoch1 / microSecondsPerSecond;
}
// 4.静态函数:获取当前系统事件对应的时间戳对象
static Timestamp now()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return Timestamp(tv.tv_sec * microSecondsPerSecond + tv.tv_usec);
}
// 5.静态函数:获取一个无效的时间戳对象
static Timestamp invalid()
{
return Timestamp();
}
static const int microSecondsPerSecond = 1000 * 1000;
private:
int64_t _microSecondsSinceEpoch1; // 以微秒为单位
};
// 时间戳⽐较重载
inline bool operator<(Timestamp lhs, Timestamp rhs)
{
return lhs.microSecondsSinceEpoch() < rhs.microSecondsSinceEpoch();
}
inline bool operator==(Timestamp lhs, Timestamp rhs)
{
return lhs.microSecondsSinceEpoch() == rhs.microSecondsSinceEpoch();
}
// 得到⼀个偏移时间
inline Timestamp addTime(Timestamp timestamp, double seconds)
{
int64_t detal = seconds * Timestamp::microSecondsPerSecond;
return Timestamp(timestamp.microSecondsSinceEpoch() + detal);
}
} // namespace net
解析: 这个 Timestamp 类是基于 Linux 系统调用封装的微秒级时间工具 ,内部使用 int64_t 存储从 1970 年至今的微秒数,通过 gettimeofday 获取高精度当前时间,支持时间格式化输出、有效性判断、时间加减与比较运算,为 Timer 定时器模块、客户端重连、网络超时处理提供稳定、统一的时间支撑,是高性能网络库必备的基础工具类。
设计知识点:
(1)typedf long time_t:一个整数类型(long/int64_t),专门来存放从1970年1月1日00:00:00UTC到现在的秒数
(2)<time.h>
cpp
struct tm {
int tm_sec; // 秒 (0-59)
int tm_min; // 分 (0-59)
int tm_hour; // 时 (0-23)
int tm_mday; // 日 (1-31)
int tm_mon; // 月 **注意:从 0 开始!0=1月,11=12月**
int tm_year; // 年 **从 1900 开始算!2025 = 125**
int tm_wday; // 星期几
int tm_yday; // 一年中第几天
int tm_isdst; // 夏令时
};
(3)struct tm* localtimer_r(const time_t* timep,struct tm* result):将秒数转换为年月日时分秒;localtime:非线程安全,内部用静态变量,多线程会乱套;localtime_r:线程安全,传入到结构体绝对安全
(4)struct timval{time_t tv_sec;//秒(从1970年到现在的秒数)
suseconds tv_usec;//微妙(0~999999)};同时存放"秒+微秒",获取高精度当前时间(int gettimeofday(struct timval* tv,struct timezone *tz);)
3. timer.h/timer.cc
Timer 是基于时间戳实现的定时任务管理组件,是网络框架中实现超时重传、客户端重连、定时关闭、心跳等功能的核心基础。
整个模块以 Timestamp 为时间基准,以 回调函数 为任务载体,支持一次性定时、周期性定时,是 Reactor 模型中必不可少的基础组件。
timer.h
cpp
#pragma once
#include "timestamp.h"
#include <set>
#include <vector>
#include <functional>
#include <atomic>
#include <assert.h>
#include "channel.h"
namespace net
{
class Timer;
class TimerQueue;
// 定时任务唯一标识,并且能够通过这个id找到定时任务
class TimerId
{
public:
TimerId(Timer *timer, int64_t seq)
: _sequence(seq), _timer(timer) {}
private:
friend class TimerQueue;
int64_t _sequence; // 定时任务唯一id
Timer *_timer; // 定时任务对象指针
};
typedef std::function<void()> TimerCallback;
// 定时任务类:超时后要做什么,什么时候超时,是否是重复性任务
class Timer
{
public:
Timer(TimerCallback functor, Timestamp when, double interval)
: _callback(std::move(functor)),
_expired(when),
_repeated(interval > 0), _interval(interval),
_sequence(_numCreated.fetch_add(1)) {}
// 执行定时任务
void run()
{
_callback();
}
int64_t sequence()
{
return _sequence;
}
// 获取过期时间
Timestamp expired()
{
return _expired;
}
// 判断是否是重复性任务
bool repeated()
{
return _repeated;
}
// 获取重复性任务间隔时间
double interval()
{
return _interval;
}
// 根据当前系统时间重置重复性任务的过期时间
void restart(Timestamp now)
{
assert(_repeated == true);
_expired = addTime(now, _interval);
}
private:
TimerCallback _callback; // 定时任务要执行的操作
Timestamp _expired; // 任务的过期时间
bool _repeated; // 标识任务是否是重复性任务
double _interval; // 重复性任务间隔时间
int64_t _sequence;
static std::atomic<int64_t> _numCreated; // 递增序号
};
// 创建定时器,获取定时器描述符
int createTimerfd();
// 计算 when - now 的时间差--->有可能要根据这个时间差重置定时器超时通知时间
struct timespec howMuchTimeFromNow(Timestamp when);
// 读取定时器时间数据(超时次数,8B) read
void readTimerfd(int timerfd, Timestamp now);
// 重置定时器超时通知时间
void resetTimerfd(int timerfd, Timestamp expiration);
// 管理所有所有的定时任务,包含超时事件的感知,能够快速找到超时任务
class TimerQueue
{
typedef std::pair<Timestamp, Timer *> Entry;
typedef std::set<Entry> TimeList;
public:
TimerQueue(EventLoop *loop);
~TimerQueue();
// 1.添加定时任务
TimerId addTimer(TimerCallback functor, Timestamp when, double interval);
// 2.取消定时任务
void cancelTimer(TimerId tid);
private:
// 在loop线程中进行实际的添加/取消操作,保证成员操作的线程安全
void addTimerInLoop(Timer *timer);
void cancelTimerInLoop(TimerId tid);
// 新增定时任务,返回标志:标识是否需要重新设置定时器超时时间
bool insert(Timer *timer);
// 过期任务的处理
// 获取所有过期的定时任务
std::vector<Entry> getExpired(Timestamp now);
// 重置过期的定时任务:针对当前过期的任务,找出重复性任务,重新添加到任务池中
void reset(std::vector<Entry> &list, Timestamp now);
// 超时事件的处理
void handleRead();
private:
EventLoop *_loop; // 事件循环对象
int _timerfd; // 定时器描述符
Channel _timerChannel; // 事件描述符对应的事件处理器
TimeList _timers;
std::atomic<bool>_callingExpiredTimers;
std::set<Timer *> _activeTimers; // 为了快速找到定时任务,判断定时任务是否在任务池中
std::set<Timer *> _cancelTimers; // 取消任务池
};
}
cpp
#include "timer.h"
#include "channel.h"
#include "eventloop.h"
#include "details.h"
#include <sys/timerfd.h>
#include <cstring>
namespace net
{
std::atomic<int64_t> Timer::_numCreated {0};
// 创建定时器,获取定时器描述符
int createTimerfd()
{
int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
if (fd < 0)
{
LOG_FATAL("timerfd_create ERROR");
}
return fd;
}
// 计算 when - now 的时间差--->有可能要根据这个时间差重置定时器超时通知时间
struct timespec howMuchTimeFromNow(Timestamp when)
{
Timestamp now = Timestamp::now();
int64_t detal = when.microSecondsSinceEpoch() - now.microSecondsSinceEpoch();
if (detal < 100)
{
detal = 100;
}
struct timespec retval;
retval.tv_sec = detal / Timestamp::microSecondsPerSecond;
retval.tv_nsec = (detal % Timestamp::microSecondsPerSecond) * 1000;
return retval;
}
// 读取定时器时间数据(超时次数,8B) read
void readTimerfd(int timerfd, Timestamp now)
{
// 定时器描述符读取数据,需要读取8字节数据,读取到的数据是超时的次数
int64_t val;
ssize_t ret = ::read(timerfd, &val, sizeof(val));
if (ret < 0)
{
LOG_ERROR("readTimerfd ERROR");
}
}
// 重置定时器超时通知时间:expiration是最近的定时任务的超时时间
void resetTimerfd(int timerfd, Timestamp expiration)
{
// int timerfd_settime(int fd,int flags,const struct itimerspec *new_value,struct itimerspec *_Nullable old_value);
struct itimerspec its;
memset(&its,0x00,sizeof(its));
its.it_value = howMuchTimeFromNow(expiration);
int ret = timerfd_settime(timerfd, 0, &its, NULL);
if (ret < 0)
{
LOG_ERROR("resetTimerfd ERROR");
}
}
// 初始化成员,将定时器添加读事件监控
TimerQueue::TimerQueue(EventLoop *loop)
: _loop(loop),
_timerfd(createTimerfd()),
_timerChannel(loop, _timerfd),
_callingExpiredTimers(false)
{
_timerChannel.setReadCallback(std::bind(&TimerQueue::handleRead, this));
_timerChannel.enableReading(); // 启动读事件监控
}
// 清理资源:解除描述符监控,移除监控,关闭描述符,释放当前还没有触发的定时任务
TimerQueue::~TimerQueue()
{
_timerChannel.disableAll();
_timerChannel.remove();
::close(_timerfd);
for (auto &entry : _timers)
{
delete entry.second;
}
}
// 1.添加定时任务:new定时任务,将定时任务添加到任务池中,返回任务ID
TimerId TimerQueue::addTimer(TimerCallback functor, Timestamp when, double interval)
{
Timer *timer = new Timer(std::move(functor), when, interval);
_loop->runInLoop(std::bind(&TimerQueue::addTimerInLoop, this, timer));
return TimerId(timer, timer->sequence());
}
// 2.取消定时任务
void TimerQueue::cancelTimer(TimerId tid)
{
_loop->runInLoop(std::bind(&TimerQueue::cancelTimerInLoop, this, tid));
}
// 在loop线程中进行实际的添加/取消操作,保证成员操作的线程安全
void TimerQueue::addTimerInLoop(Timer *timer)
{
_loop->assertInLoopThread();
// 调用insert插入定时任务,根据返回值判断是否需要重置定时器超时时间
bool changed = insert(timer);
if (changed)
{
// 获取定时任务的超时时间,重置定时器
resetTimerfd(_timerfd, timer->expired());
}
}
void TimerQueue::cancelTimerInLoop(TimerId tid)
{
// 1.从任务池中删除定时任务
assert(_activeTimers.size() == _timers.size());
if (_activeTimers.find(tid._timer) != _activeTimers.end())
{
Entry entry(tid._timer->expired(), tid._timer);
_timers.erase(entry);
_activeTimers.erase(tid._timer);
delete tid._timer;
}
else if (_callingExpiredTimers == true)
{
// 2.取消任务的时候,任务不在任务池中,而是取消的任务就是当前正在处理的超时任务(在内部取消了自己)
// 将任务添加到取消池中
_cancelTimers.insert(tid._timer);
}
assert(_activeTimers.size() == _timers.size());
}
// 新增定时任务,返回标志:标识是否需要重新设置定时器超时时间
bool TimerQueue::insert(Timer *timer)
{
_loop->assertInLoopThread();
assert(_activeTimers.size() == _timers.size());
bool changed = false;
Timestamp t = timer->expired();
// 2.如果添加的节点就是最小节点,意味着需要重置定时器
if (_timers.empty() || t < _timers.begin()->second->expired())
{
changed = true;
}
// 1.将任务向任务池和查找池中分别添加一份
auto ret1 = _timers.insert(Entry(t, timer));
assert(ret1.second);
auto ret2 = _activeTimers.insert(timer);
assert(ret2.second);
assert(_activeTimers.size() == _timers.size());
return changed;
}
// 过期任务的处理
void TimerQueue::handleRead()
{
_loop->assertInLoopThread();
Timestamp now = Timestamp::now();
// 1.读取超时事件数据
readTimerfd(_timerfd, now);
// 2.获取所有的超时任务:以当前时间构造一个entry对象,从_timer查找比他小的节点
std::vector<Entry> expireds = getExpired(now);
// 3.执行处理所有的定时任务:回调任务中的回调函数
_callingExpiredTimers = true;
_cancelTimers.clear(); // 清空以前的特殊取消数据
for (auto &entry : expireds)
{
entry.second->run();
}
_callingExpiredTimers = false;
// 4.重置定时任务:将重复性的定时任务,重置时间后,重新添加到任务池中
reset(expireds, now);
}
// 获取所有过期的定时任务
std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
{
assert(_activeTimers.size() == _timers.size());
// 任务池用的是红黑树,获取过期任务,就是找到小于当前事件节点的数据节点
std::vector<TimerQueue::Entry> retval;
// 1.根据当前时间,组织一个entry节点,必须是最大的时间节点
TimerQueue::Entry entry(now, (Timer *)UINTPTR_MAX);
// 2.根据组织的entry节点从任务池中取出所有小于该节点的数据
auto end = _timers.lower_bound(entry); // 获取到第一个大于等于entry的节点,小于end的节点都是过期任务节点
// 一种情况是要么任务池是空的,要么所有任务都是过期任务;另一种情况是end这个节点的过期时间必然大于当前时间
assert(end == _timers.end() || now < end->first);
std::copy(_timers.begin(), end, std::back_inserter(retval));
// 3.从任务池&&查找池中删除这些任务节点
_timers.erase(_timers.begin(), end);
for (const auto &it : retval)
{
int n = _activeTimers.erase(it.second);
assert(n == 1);
}
assert(_activeTimers.size() == _timers.size());
// 4.返回结果
return retval;
}
// 重置过期的定时任务:针对当前过期的任务,找出重复性任务,重新添加到任务池中
void TimerQueue::reset(std::vector<Entry> &list, Timestamp now)
{
// 遍历过期任务,判断任务是否是重复性任务
for (auto &entry : list)
{
// 如果是重复性任务,且没有被取消,则重置过期时间,重新添加到任务池
if (entry.second->repeated() && _cancelTimers.find(entry.second) == _cancelTimers.end())
{
entry.second->restart(now);
_timers.insert(Entry(entry.second->expired(), entry.second));
_activeTimers.insert(entry.second);
}
else
{
// 如果不是,则释放任务节点
delete entry.second;
}
}
// 从任务池中找到最小的节点,将这个节点的过期时间设置为定时器的通知时间
Timestamp t; // 这个变量是一个无效时间
if (!_timers.empty())
{
t = _timers.begin()->second->expired();
}
if (t.valid())
{
resetTimerfd(_timerfd, t);
}
}
// 超时事件的处理
}
**解析:**class TimerId:定时任务唯一标识,并且能够通过这个id找到定时任务;class Timer:定时任务类,超时后要做什么,什么时候超时,是否是重复性任务;class TimerQueue:管理所有的定时任务,包含超时事件的感知,能够快速找到超时任务;可以将TimerQueue堪称三个任务池:正在运行的任务池(没到期,排队中)、过期任务池(时间到了,拿出来执行)、取消任务池(执行中自己取消自己)。
涉及知识点:
(1)std::pair:就是一对数据,把两个变量打包成一个对象;pair比较时,先比较第一个元素(Timestamp),如果相等再比第二个元素
(2)std::set:C++标准库的有序集合,自动排序,自动去重,插入、删除、查找效率极高O(logN),最小的元素永远在begin()
(3)定时器设置结构体:
struct itimersprc{struct timespec it_interval;//间隔时间,周期性定时
struct timespec it_value;//第一次超时时间};
(4)设置超时时间:int timefd_settime(int fd;int flags,const struct itimespec* new_value,struct itimespec *old_value);int flags:相对时间(0),绝对时间(TFD_TIMER_ABSTIME)
(5)std::set::lower_bound():在一个已经排好的集合里,找到第一个大于等于目标值的元素位置
(6)std::back_inserter(头文件#include<algorithm>):只能插入器,专门用来vector等容器尾部追加元素的工具
4. Poller.h/Ppller.cc
poller.h
cpp
#pragma once
#include <sys/epoll.h>
#include <vector>
#include <map>
#include "timestamp.h"
namespace net
{
class Channel;
class EventLoop;
class Timestamp;
class Poller
{
public:
typedef std::vector<Channel *> ChannelList;
Poller(EventLoop *loop) : _ownerLoop(loop) {}
virtual ~Poller() = default;
// 获取就绪channel数组
virtual Timestamp poll(int timeoutMs, ChannelList *activeChannels) = 0;
// 新增或修改或移除channel的事件监控
virtual void updateChannel(Channel *channel) = 0;
// 移除channel的事件监控以及channel管理
virtual void removeChannel(Channel *channel) = 0;
// 判断channel是否在管理中
virtual bool hasChannel(Channel *channel) const;
// 创建默认的Poller对象
static Poller *newDefaultPoller(EventLoop *loop);
protected:
typedef std::map<int, Channel *> ChannelMap;
ChannelMap _channels;
private:
EventLoop *_ownerLoop;
};
namespace
{
// Channel的监控状态
const int kNew = -1; // channel是新建的
const int kAdded = 1; // channel已添加监控
const int kDeleted = 2; // channel被移除了监控
}
namespace
{
static const int kNoneEvent = 0;
static const int kReadEvent = EPOLLIN | EPOLLPRI;
static const int kWriteEvent = EPOLLOUT;
}
class EPollPoller : public Poller
{
public:
EPollPoller(EventLoop *loop);
~EPollPoller() override;
// 获取就绪channel列表,⽐较特殊的是这⾥⽤了动态数组,如果满载就扩容
Timestamp poll(int timeoutMs, ChannelList *activeChannels) override;
// 如果channel状态是 kNew || kDeleted 就添加监控,如果是kNew 还要添加管理
// 添加完毕后,将channel状态设置为 kAdded
// 如果是 kAdded 还更新,监控事件是0就移除监控,并置位kDeleted,否则就是更新事件
void updateChannel(Channel *channel) override;
// 从管理中移除channel,并移除channel的事件监控,置位channel状态为kNew
void removeChannel(Channel *channel) override;
private:
// 从EventList中获取就绪事件的channel,设置就绪事件,加⼊activeChannels中
void fillActiveChannels(int numEvents, ChannelList *activeChannels) const;
// 实际的epoll操作接⼝
void update(int operation, Channel *channel);
private:
static const int kInitEventListSize = 16;
typedef std::vector<struct epoll_event> EventList;
int _epollfd;
EventList _events;
};
}
cpp
#include "poller.h"
#include "details.h"
#include "channel.h"
#include <cstring>
#include <errno.h>
#include <unistd.h>
#include <cassert>
namespace net
{
int createEpoll()
{
int efd = epoll_create1(EPOLL_CLOEXEC);
if (efd < 0)
{
LOG_FATAL("创建epoll失败:%s", strerror(errno));
}
return efd;
}
bool Poller::hasChannel(Channel *channel) const
{
if (_channels.find(channel->fd()) != _channels.end())
{
return true;
}
return false;
}
Poller *Poller::newDefaultPoller(EventLoop *loop)
{
return new EPollPoller(loop);
}
// 初始化成员
EPollPoller::EPollPoller(EventLoop *loop)
: Poller(loop), _epollfd(createEpoll()), _events(kInitEventListSize)
{
}
// 关闭epoll
EPollPoller::~EPollPoller()
{
::close(_epollfd);
}
// 获取就绪channel列表,⽐较特殊的是这⾥⽤了动态数组,如果满载就扩容
Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels)
{
Timestamp now = Timestamp::now();
int ret = epoll_wait(_epollfd, &_events[0], _events.size(), timeoutMs);
if (ret < 0)
{
if (errno == EINTR)
{
LOG_DEBUG("epoll被信号中断");
return now;
}
LOG_ERROR("epoll wait 错误:%s", strerror(errno));
return now;
}
else if (ret == 0)
{
//LOG_DEBUG("epoll wait 超时");
return now;
}
fillActiveChannels(ret, activeChannels);
if (ret == _events.size())
{
_events.resize(_events.size() * 2);
}
return now;
}
void EPollPoller::updateChannel(Channel *channel)
{
// 如果channel的index状态进行不同处理
// kNew,kDeleted表示channel描述符没有被监控
// kAdded 表示监控中
if (channel->index() == kNew || channel->index() == kDeleted)
{
int fd = channel->fd();
if (channel->index() == kNew)
{
assert(_channels.find(fd) == _channels.end());
_channels[fd] = channel;
}
else
{
// 当前描述符是移除监控状态
assert(_channels.find(fd) != _channels.end());
assert(_channels[fd] == channel);
}
update(EPOLL_CTL_ADD, channel);
channel->set_index(kAdded);
}
else
{
if (channel->events() == kNoneEvent)
{
update(EPOLL_CTL_DEL, channel);
channel->set_index(kDeleted);
}
else
{
update(EPOLL_CTL_MOD, channel);
}
}
}
void EPollPoller::update(int operation, Channel *channel)
{
int fd = channel->fd();
int events = channel->events();
struct epoll_event ev;
ev.events = events;
ev.data.ptr = channel;
int ret = epoll_ctl(_epollfd, operation, fd, &ev);
if (ret < 0)
{
LOG_ERROR("epoll_ctl 错误:%s", strerror(errno));
}
}
void EPollPoller::removeChannel(Channel *channel)
{
// 从epol1中移除监控
int fd = channel->fd();
assert(_channels.find(fd) != _channels.end());
assert(_channels[fd] == channel);
if (channel->index() == kAdded)
{
update(EPOLL_CTL_DEL, channel);
}
// 从_channels中移除管理
_channels.erase(channel->fd());
channel->set_index(kNew);
}
void EPollPoller::fillActiveChannels(int numEvents, ChannelList *activeChannels) const
{
// 从成员_events中取出就绪的事件,从事件中取出channel,添加到activeChannels
for (int i = 0; i < numEvents; ++i)
{
Channel *channel = (Channel *)_events[i].data.ptr;
channel->setREvents(_events[i].events); // 设置实际就绪的事件
activeChannels->push_back(channel); // 添加返回信息
}
}
}
**解析:**class Poller:抽象基类(接口),不管是用select、poll还是epoll都继承该类;class EpollPoller:public Poller:基于epoll的真正实现,用epoll监听一堆fd,谁就绪了,就把谁返回给EventLoop。
涉及知识点:
(1)map与unoreder_map:map(红黑树)稳定,查找慢一点,有序;unoreder_map(哈希表)无序,查找超快,可能冲突。这里用map,稳定不冲突。
(2)epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);等待事件发生(阻塞等待),int epfd:epoll 句柄,struct epoll_event *events:输出参数,内核会把发生了事件的 fd 填到这里,int maxevents:你提供的数组最大能装多少个事件,就是 events 数组的大小,int timeout:超时时间(毫秒)取值:1:永远阻塞,直到有事件,0:不阻塞,立即返回,>0:等待指定毫秒数。
(3)struct epoll_event
{
uint32_t events;//你要监听的时间(读/写/错误)
epoll_data_t data;//你自己帮的那个的数据(指针/fd)
};
(4)epoll_ctl(int epfd,int opreation,int fd,struct epoll_event* event):epoll 的 "控制面板" ,增 / 删 / 改 监听的文件描述符全靠它;int operation:要做什么操作,只有 3 种:
EPOLL_CTL_ADD:添加 一个 fd 到 epoll
EPOLL_CTL_DEL:删除 一个 fd 从 epoll
EPOLL_CTL_MOD:修改 监听的事件(比如从只读 → 读写)
5. channel.h/channel.cc
封装 fd 和其关注的事件,绑定事件回调,是 EventLoop 和 fd 之间的桥梁:
channel.h
cpp
#pragma once
#include <memory>
#include <functional>
#include "poller.h"
namespace net
{
typedef std::function<void()> EventCallback;
typedef std::function<void(Timestamp)> ReadEventCallback;
class Channel
{
public:
Channel(EventLoop *loop, int fd);
~Channel();
// 设置事件回调处理函数
void setReadCallback(ReadEventCallback cb) { _readCallback = std::move(cb); }
void setWriteCallback(EventCallback cb) { _writeCallback = std::move(cb); }
void setCloseCallback(EventCallback cb) { _closeCallback = std::move(cb); }
void setErrorCallback(EventCallback cb) { _errorCallback = std::move(cb); }
// 启动当前channel中描述符的读事件监控
void enableReading()
{
_events |= kReadEvent; // 修改当前要监控的事件
update(); // poller->updatechannel(this);epoll_ctl(ADD, fd, events);
}
void disableReading()
{
_events &= ~kReadEvent; // 将kReadEvent位置0,其他位不变
update();
}
void enableWriting()
{
_events |= kWriteEvent;
update();
}
void disableWriting()
{
_events &= ~kWriteEvent;
update();
}
void disableAll()
{
_events = kNoneEvent;
update();
}
bool isWriting() const
{
return _events & kWriteEvent;
}
bool isReading() const
{
return _events & kReadEvent;
}
// 设置channel监控状态
int index() { return _index; }
void set_index(int idx) { _index = idx; }
// 获取当前channel所在的事件循环
EventLoop *ownerLoop() { return _loop; }
// 设置channel的外部管理对象,通过_tie成员观察外部对象是否已经被释放
void tie(const std::shared_ptr<void> &obj)
{
_tie = obj;
_tied = true;
}
// 获取监控的描述符
int fd() const { return _fd; }
// 获取监控中的事件
int events() const { return _events; }
// 设置描述符实际就绪的事件,在Poller中设置的
void setREvents(int revt) { _revents = revt; }
// 判断当前描述符是否还有监控的事件
bool isNoneEvent() const { return _events == kNoneEvent; }
// 移除监控&管理
// 总体的事件处理:根据不同的实际就绪的事件,调用不同的回调函数进行事件处理
void handleEvent(Timestamp receiveTime)
{
if (_tied)
{
// 如果设置了观察者对象,主要针对的是Connection
if (_tie.lock())
{
// 通过观察者对象观察外部对象是否被释放
handleEventWithGuard(receiveTime);
}
}
else
{
// 主要针对的是Acceptor,TimerQueue
handleEventWithGuard(receiveTime);
}
}
void remove();
private:
void update();
void handleEventWithGuard(Timestamp receiveTime)
{
// 针对不同事件进行不同的处理
// 1.若连接挂断,且没有出发可读事件:直接handleClose
_eventHandling = true;
if (_revents & EPOLLHUP && !(_revents & EPOLLIN))
{
if (_closeCallback)
_closeCallback();
}
// 2.触发了可读事件:EPoLLIN|EPOLLPRI,调用handleRead
if (_revents & (EPOLLIN | EPOLLPRI) || _revents & EPOLLRDHUP)
{
if (_readCallback)
_readCallback(receiveTime);
}
// 3.触友了可写事件:EPOLLOUT调用handleWrite
if (_revents & EPOLLOUT)
{
if (_writeCallback)
_writeCallback();
}
// 4.触发了错误事件:EPOLLERR 调用handleError
if (_revents & EPOLLERR)
{
if (_errorCallback)
_errorCallback();
}
_eventHandling = false;
}
private:
EventLoop *_loop; // 当前channel所在的线程事件循环
int _fd; // 要监控的描述符
int _events; // 要监控的事件
int _revents; // 实际就绪的事件
int _index; // 状态描述:当前channel对应的描述符是否在poller中添加了监控 epol1_ctl
// 观察者模式的另类使用
bool _tied;
std::weak_ptr<void> _tie; // 指向外部管理对象 Acceptor / Connection/ TimerQueue
bool _eventHandling; // 描述当前处理状态:是否处于事件处理中
bool _addedToLoop; // 状态描述:描述当前channel是否在poller的管理中
ReadEventCallback _readCallback; // 读事件处理回调
EventCallback _writeCallback; // 写事件回调
EventCallback _closeCallback; // 关闭事件回调
EventCallback _errorCallback; // 错误事件回调
};
}
cpp
#include "channel.h"
#include "eventloop.h"
#include "details.h"
#include <cassert>
namespace net
{
Channel::Channel(EventLoop *loop, int fd) : _loop(loop),
_fd(fd),
_events(kNoneEvent),
_revents(kNoneEvent),
_index(kNew),
_tied(false),
_eventHandling(false),
_addedToLoop(false)
{
//LOG_DEBUG("NEW Channel: %lu", (uint64_t)this);
}
Channel::~Channel()
{
assert(_eventHandling == false);
assert(_addedToLoop == false);
//LOG_DEBUG("DELETE Channel:%lu", (uint64_t)this);
}
void Channel::remove()
{
_loop->removeChannel(this);
_addedToLoop = false;
}
void Channel::update()
{
_addedToLoop = true;
_loop->updateChannel(this);
}
}
**解析:**class channel;事件处理模块,一个连接(fd的包装);记录监听事件,记录回调函数,事件来了自己分发处理,只属于一个EventLoop(线程安全)。
设计知识点:
(1)<functional>封装,存储,传递可调用对象(C++回调机制专用库)
①std::function:通用函数包装器(函数、函数指针、lambda表达式、类成员函数、仿函数),就是将所有能调用的东西包装成一个变量。
②std::bind:函数绑定适配器;格式std::bind(函数地址,对象/this,参数1,参数2);将不符合传入的函数之类的业务函数改成符合要求的格式。
(2)位操作
①|=:按位或赋值,通俗一点就是打开某个开关(设为1);口诀:有1得1
②&=:按位与赋值,通俗一点就是关闭某个开关(设为0);口诀:有0得0
6. EventLoop.h/EventLoop.cc
Reactor 的核心,负责驱动 epoll_wait、分发事件、管理 Channel:
eventloop.h
cpp
#pragma once
#include <vector>
#include <atomic>
#include <memory>
#include <mutex>
#include <functional>
#include <unistd.h>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <signal.h>
#include "timestamp.h"
#include "poller.h"
#include "timer.h"
namespace net
{
class IgnoreSigPipe
{
public:
IgnoreSigPipe()
{
signal(SIGPIPE,SIG_IGN);
}
};
static IgnoreSigPipe ignore_pipe;
const int InitEpollTimeout = 1000;
class Channel;
// 事件循环类:事件监控+任务池+定时器
class EventLoop
{
typedef std::function<void()> Functor;
public:
// 初始化成员
EventLoop();
~EventLoop();
// 开始事件循环:1.开始描述符事件监控,2.处理任务池的任务
void loop();
// 设置_quit标志,如果当前处于事件监控中,则唤醒循环,退出循环
void quit();
// 如果当前就在loop所在线程中,则直接执⾏,否则就添加到任务池
void runInLoop(Functor cb);
// 将任务添加到任务池中,如果当前没有在loop线程中,或者当前处于处理任务池任务状态则唤醒
void queueInLoop(Functor cb);
// 唤醒事件监控:向_weakupfd写入数据
void wakeup();
// 通过poller添加监控
void updateChannel(Channel *channel);
// 通过poller移除监控
void removeChannel(Channel *channel);
// 判断channel是否添加过监控管理
bool hasChannel(Channel *channel);
// 断言操作:当前的操作是否在loop所在的线程中
void assertInLoopThread();
// 判断当前是否处于loop所在的线程中
bool isInLoopThread() const;
// 提供三种不同的接口添加定时任务:定点,延迟,循环
// 通过timer添加指定时间的定时任务
TimerId runAt(Timestamp time, TimerCallback cb);
// 通过timer添加延迟N秒的定时任务
TimerId runAfter(double delay_sec, TimerCallback cb);
// 通过timer添加循环定时任务
TimerId runEvery(double interval_sec, TimerCallback cb);
private:
// 针对_weakupfd读取数据
void handleRead();
// 处理任务池中的任务
void doPendingFunctors();
private:
typedef std::vector<Channel *> ChannelList;
bool _looping; // 状态描述:描述当前事件循环是否处于循环中
std::atomic<bool> _quit; // 退出标志,事件循环的循环体条件
bool _eventHandling; // 状态描述:当前是否正在事件处理中
bool _callingPendingFunctors; // 状态描述:当前事件循环是否处于执行任务池状态
pid_t _threadId; // 线程Id pthread_self,pid_t gettid()==getpid()
Timestamp _pollReturnTime; // 获取poll监控返回时的时间
std::unique_ptr<Poller> _poller; // 事件监控器
std::unique_ptr<TimerQueue> _timerQueue; // 定时器
int _wakeupFd; // 事件描述符,主要用于唤醒事件监控
std::unique_ptr<Channel> _wakeupChannel; //_wakeupFd对应的channel
ChannelList _activeChannels; // channel*数组,用于获取就绪的channel
Channel *_currentActiveChannel; // 从_activeChannels获取当前要处理的channel
std::mutex _mutex; // 任务池的保护锁
std::vector<Functor> _pendingFunctors; // 任务池
};
class EventLoopThread
{
public:
EventLoopThread();
~EventLoopThread();
EventLoop *startLoop(); // 获取时间循环对象的指针
private:
void threadFunc();
private:
EventLoop *_loop; // 事件循环对象的指针
std::mutex _mutex; // 互斥锁
std::condition_variable _cond; // 条件变量
std::thread _thread; // 线程
};
class EventLoopThreadPool
{
public:
EventLoopThreadPool(EventLoop* baseloop);
~EventLoopThreadPool();
void setThreadNum(int count);
void start();
EventLoop* getNextLoop();
private:
int _thread_num;//线程数量
int _next_idx;//负载均衡索引
EventLoop* _baseloop;//使用者定义的eventloop对象
std::vector<std::unique_ptr<EventLoopThread>> _threads;
std::vector<EventLoop*>_loops;
};
} // namespace net
cpp
#include "eventloop.h"
#include "poller.h"
#include "channel.h"
#include "details.h"
#include <cassert>
#include <cstring>
#include <errno.h>
#include <sys/eventfd.h>
namespace net
{
int createEventFd()
{
// 创建eventfd对象,用于事件唤醒
// EFD_CLOEXEC:fork后在子进程中关闭描述符
int fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (fd < 0)
{
LOG_FATAL("eventfd 失败: %s", strerror(errno));
}
return fd;
}
void writeEventFd(int fd)
{
uint64_t val = 1;
ssize_t ret = ::write(fd, &val, sizeof(val));
if (ret < 0)
{
LOG_ERROR("event fd write error: %s", strerror(errno));
}
}
void readEventFd(int fd)
{
uint64_t val;
ssize_t ret = ::read(fd, &val, sizeof(val));
if (ret < 0)
{
LOG_ERROR("event fd read error: %s", strerror(errno));
}
}
EventLoop::EventLoop() : _looping(false),
_quit(false),
_eventHandling(false),
_callingPendingFunctors(false),
_threadId(::gettid()), // 决定了eventloop对象必须在所在线程中实例化
_poller(Poller::newDefaultPoller(this)),
_timerQueue(new TimerQueue(this)),
_wakeupFd(createEventFd()),
_wakeupChannel(new Channel(this, _wakeupFd))
{
// 为_wakeupFd触发的可读事件设置回调处理函数
_wakeupChannel->setReadCallback(std::bind(&EventLoop::handleRead, this));
// 将_wakeupFd挂到Poller中进行监控
_wakeupChannel->enableReading();
}
EventLoop::~EventLoop()
{
assert(_looping == false);
if (_poller->hasChannel(_wakeupChannel.get()))
{
_wakeupChannel->disableAll();
_wakeupChannel->remove();
}
::close(_wakeupFd);
}
// 开始事件循环:1.开始描述符事件监控,2.处理任务池的任务
void EventLoop::loop()
{
_looping = true;
_quit = false;
while (!_quit)
{
// 获取就绪描述符channel,进行事件处理
_activeChannels.clear();
Timestamp now = _poller->poll(InitEpollTimeout, &_activeChannels);
_currentActiveChannel = NULL;
_eventHandling = true;
for (int i = 0; i < _activeChannels.size(); ++i)
{
_currentActiveChannel = _activeChannels[i]; // 拿到就绪channel
_currentActiveChannel->handleEvent(now);
}
_eventHandling = false;
// 处理任务池中的任务
doPendingFunctors();
}
_looping = false;
}
// 设置_quit标志,如果当前处于事件监控中,则唤醒循环,退出循环
void EventLoop::quit()
{
_quit = true;
// 如果当前没有在1oop线程中则唤醒
if (isInLoopThread())
{
wakeup(); // 唤醒事件监控
}
}
// 如果当前就在loop所在线程中,就直接执行,否则将任务压入任务池中
void EventLoop::runInLoop(Functor cb)
{
if (isInLoopThread())
{
cb();
}
else
{
queueInLoop(cb);
}
}
// 将任务压入任务池中,唤醒事件监控
void EventLoop::queueInLoop(Functor cb)
{
{
std::unique_lock<std::mutex> lock(_mutex);
_pendingFunctors.push_back(std::move(cb));
}
if (_callingPendingFunctors || isInLoopThread())
{
wakeup(); // 唤醒事件监控
}
}
// 唤醒事件监控:向_wakeupFd写入数据
void EventLoop::wakeup() { writeEventFd(_wakeupFd); }
// 通过poller添加监控
void EventLoop::updateChannel(Channel *channel)
{
assertInLoopThread();
_poller->updateChannel(channel);
}
// 通过poller移除监控
void EventLoop::removeChannel(Channel *channel)
{
assertInLoopThread();
_poller->removeChannel(channel);
}
// 判断channel是否添加过监控管理
bool EventLoop::hasChannel(Channel *channel)
{
assertInLoopThread();
return _poller->hasChannel(channel);
}
// 断言操作:当前的操作是否在1oop所在线程中
void EventLoop::assertInLoopThread() { assert(_threadId == ::gettid()); }
// 判断当前是否处于loop所在线程中
bool EventLoop::isInLoopThread() const
{
if (_threadId == ::gettid())
{
return true;
}
return false;
}
// 针对_wakeupFd读取数据
void EventLoop::handleRead() { readEventFd(_wakeupFd); }
// 处理任务池中的任务
void EventLoop::doPendingFunctors()
{
_callingPendingFunctors = true;
std::vector<Functor> functors;
{
std::unique_lock<std::mutex> lock(_mutex);
functors.swap(_pendingFunctors);
}
for (auto &functor : functors)
{
functor();
}
_callingPendingFunctors = false;
}
// 通过timer添加指定时间的定时任务
TimerId EventLoop::runAt(Timestamp time, TimerCallback cb)
{
return _timerQueue->addTimer(std::move(cb), time, 0);
}
// 通过timer添加延迟N秒的定时任务
TimerId EventLoop::runAfter(double delay_sec, TimerCallback cb)
{
// 1.根据当前系统时间,计算得到超时时间点
Timestamp when = net::addTime(Timestamp::now(), delay_sec);
// 2.添加定时任务
return _timerQueue->addTimer(std::move(cb), when, 0);
}
// 通过timer添加循环定时任务
TimerId EventLoop::runEvery(double interval_sec, TimerCallback cb)
{
Timestamp when = net::addTime(Timestamp::now(), interval_sec);
return _timerQueue->addTimer(std::move(cb), when, interval_sec);
}
EventLoopThread::EventLoopThread()
: _loop(NULL),
_thread(std::thread(&EventLoopThread::threadFunc, this))
{
}
EventLoopThread::~EventLoopThread()
{
// 1.退出事件循环
_loop->quit();
// 2.等待线程退出
_thread.join();
}
// 获取时间循环对象的指针
EventLoop *EventLoopThread::startLoop()
{
// 当获取loop对象指针的时候,必须保证_loop已经赋值完毕
std::unique_lock<std::mutex> lock(_mutex);
_cond.wait(lock, [this]()
{ return _loop != NULL; });
return _loop;
}
void EventLoopThread::threadFunc()
{
// 实例化了一个局部的eventloop对象
EventLoop loop;
{
std::unique_lock<std::mutex> lock(_mutex);
_loop = &loop;
_cond.notify_all(); // 唤醒所有的阻塞等待
}
loop.loop(); // 开始事件循环
_loop = NULL;
}
EventLoopThreadPool::EventLoopThreadPool(EventLoop *baseloop)
: _thread_num(0),
_next_idx(0),
_baseloop(baseloop) {}
EventLoopThreadPool::~EventLoopThreadPool() {}
void EventLoopThreadPool::setThreadNum(int count)
{
_thread_num = count;
}
void EventLoopThreadPool::start()
{
_baseloop->assertInLoopThread();
// 根据数量设置,创建制定数量的Loopthread
// 根据创建的Loopthread,初始化添加loop和thread成员
for (int i = 0; i < _thread_num; ++i)
{
EventLoopThread *loopThread = new EventLoopThread();
_threads.push_back(std::unique_ptr<EventLoopThread>(loopThread));
_loops.push_back(loopThread->startLoop());
}
}
EventLoop *EventLoopThreadPool::getNextLoop()
{
_baseloop->assertInLoopThread();
// RR轮转负载均衡策略
if (_thread_num == 0)
{
return _baseloop;
}
EventLoop *retval = _loops[_next_idx];
_next_idx++;
if (_next_idx == _loops.size())
{
_next_idx = 0;
}
return retval;
}
}
**解析:**class IgnoreSigPipe:忽略SIGPIPE信号,当想已经关闭的socket写数据时,系统回发SIGPIPE信号直接杀死进程,这里忽略它,防止服务器直接崩溃;class EvenLoop:一个线程=一个EventLoop=一个Poller;class EventLoopThread:创建一个线程,线程里跑一个EventLoop::loop(),one loop per thread;class EventLoopThreadPool:主从Reactor核心,主loop接连接,子loop处理读写。
涉及知识点:
(1)<atomic>原子操作:保证多线程下修改变量安全,不加锁也不会出错
(2)<sys/eventfd.h>创建eventfd:一个纯计数用的轻量级fd,专用来发通知,唤醒线程
(3)<cstdint>无符号整型:uint8_t,8位无符号整数,1字节;uint16_t,16位无符号整数,2字节;uint32_t,32位无符号整数,4字节;uint64_t,64位无符号整数,8字节;位数绝对固定,不像long/int会随平台变化
(4)RR轮询(Round Robin):按顺序挨个轮流分配,分到末尾再从头循环,平均分发任务,做到负载均衡
7.socket.h/socket.cc
Socket 类是对 Linux 套接字 API 的面向对象封装,隐藏了 socket、bind、listen、accept、connect 等底层系统调用,为 Acceptor 和 TcpConnection 提供稳定、安全、非阻塞的网络通信通道,是整个网络框架与内核交互的最底层接口。
socket.h
cpp
#pragma once
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
namespace net
{
namespace sockets
{
static const int LISTEN_COUNT = 1024;
// 创建⾮阻塞套接字,失败则抛异常
int createNonblockSocket();
// 创建普通默认套接字,失败则抛异常
int createBlockSocket();
// 为套接字绑定地址,失败则抛异常
void bind(int sockfd, const struct sockaddr *addr);
// 直接返回系统调⽤即可,失败由外部处理
int connect(int sockfd, const struct sockaddr *addr);
// 开始监听套接字,失败抛异常
void listen(int sockfd);
// 获取新连接,accept4可以获取的同时设置套接字选项
// 错误情况: 可接受:EAGAIN,ECONNABORTED,EINTR,EPROTO,EPERM,EMFILE
// 不可接收抛异常:EBADF,EFAULT,EINVAL,ENFILE,ENOBUFS,ENOMEM,ENOTSOCK,EOPNOTSUPP
int accept(int sockfd, struct sockaddr_in *addr);
// 直接返回系统调⽤
ssize_t read(int fd, void *buf, size_t size);
// 直接返回系统调⽤
ssize_t readv(int fd, struct iovec *vec, int count);
// 直接返回系统调⽤
ssize_t write(int fd, const void *buf, size_t size);
// 直接返回系统调⽤
void close(int fd);
// 转换为:192.168.1.1:8080 inet_ntop
void toIpPort(char *buf, size_t size, const struct sockaddr_in *addr);
// 转为⽹络字节序地址结构数据 inet_pton
void fromIpPort(const char *ip, uint16_t port, struct sockaddr_in *addr);
}
class InetAddress
{
public:
// 初始化数据,INADDR_LOOPBACK / INADDR_ANY
explicit InetAddress(uint16_t port = 0);
// 初始化数据
InetAddress(const std::string ip, uint16_t port);
// 地址转字符串
std::string toIpPort() const;
// 获取地址数据
const struct sockaddr *getSockAddr() const;
// 设置地址数据
void setSockAddr(struct sockaddr_in addr);
private:
struct sockaddr_in _addr;
};
class Socket
{
public:
explicit Socket(int sockfd) : _sockfd(sockfd) {}
~Socket() { sockets::close(_sockfd); }
int fd() { return _sockfd; }
void bind(const InetAddress &localaddr);
void listen();
int accept(InetAddress *peeraddr);
// IPPROTO_TCP, TCP_NODELAY
void setTcpNoDelay(bool on);
// SOL_SOCKET, SO_REUSEADDR
void setReuseAddr(bool on);
// SOL_SOCKET, SO_REUSEPORT
void setReusePort(bool on);
// SOL_SOCKET, SO_KEEPALIVE
void setKeepAlive(bool on);
private:
const int _sockfd;
};
}
cpp
#include "socket.h"
#include "details.h"
#include <sys/uio.h>
#include <cstring>
#include <linux/tcp.h>
namespace net
{
// 创建⾮阻塞套接字,失败则抛异常
int sockets::createNonblockSocket()
{
int fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
if (fd < 0)
{
LOG_FATAL("创建套接字失败");
}
return fd;
}
// 创建普通默认套接字,失败则抛异常
int sockets::createBlockSocket()
{
int fd = ::socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
if (fd < 0)
{
LOG_FATAL("创建套接字失败");
}
return fd;
}
// 为套接字绑定地址,失败则抛异常
void sockets::bind(int sockfd, const struct sockaddr *addr)
{
int ret = ::bind(sockfd, addr, sizeof(struct sockaddr_in));
if (ret < 0)
{
LOG_FATAL("套接字绑定地址失败");
}
}
// 直接返回系统调⽤即可,失败由外部处理
int sockets::connect(int sockfd, const struct sockaddr *addr)
{
int ret = ::connect(sockfd, addr, sizeof(struct sockaddr_in));
if (ret < 0)
{
LOG_ERROR("连接服务器失败");
}
return ret;
}
// 开始监听套接字,失败抛异常
void sockets::listen(int sockfd)
{
int ret = ::listen(sockfd, LISTEN_COUNT);
if (ret < 0)
{
LOG_FATAL("监听失败");
}
}
// 获取新连接,accept4可以获取的同时设置套接字选项
// 错误情况: 可接受:EAGAIN,ECONNABORTED,EINTR,EPROTO,EPERM,EMFILE
// 不可接收抛异常:EBADF,EFAULT,EINVAL,ENFILE,ENOBUFS,ENOMEM,ENOTSOCK,EOPNOTSUPP
int sockets::accept(int sockfd, struct sockaddr_in *addr)
{
socklen_t len = sizeof(struct sockaddr_in);
int ret = ::accept4(sockfd, (struct sockaddr *)addr, &len, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (ret < 0)
{
switch (errno)
{
case EAGAIN: // 非阻塞场景下,没有新连接
case ECONNABORTED: // 新的连接异常
case EINTR: // 当前的阻塞操作被信号中断了
case EPROTO: // 协议错误
case EPERM: // 防火墙拦截
case EMFILE: // 文件描述符到达进程的限制上限
break;
case EBADF: // 坏的文件描述符
case EFAULT: // 地址参数无效
case EINVAL: // 参数无效
case ENFILE: // 系统描述符达到上限
case ENOBUFS: // 内存不足
case ENOTSOCK: // 描述符不是一个套接字描述符
case EOPNOTSUPP: // 操作操作,描述符不是一个流式套接字
LOG_FATAL("获取新连接失败");
default:
LOG_FATAL("获取新连接失败");
}
}
return ret;
}
// 直接返回系统调⽤
ssize_t sockets::read(int fd, void *buf, size_t size)
{
return ::read(fd, buf, size); //==recv(fd,buf,size,0)
}
// 直接返回系统调⽤
ssize_t sockets::readv(int fd, struct iovec *vec, int count)
{
// 实现分块接收,将接收的数据放到不连续的内存空间中
return ::readv(fd, vec, count);
}
// 直接返回系统调⽤
ssize_t sockets::write(int fd, const void *buf, size_t size)
{
return ::write(fd, buf, size); //==send(fd,buf,size,0)
}
// 直接返回系统调⽤
void sockets::close(int fd)
{
if (fd > 0)
{
::close(fd);
}
}
// 转换为:192.168.1.1:8080 inet_ntop
void sockets::toIpPort(char *buf, size_t size, const struct sockaddr_in *addr)
{
// 转换IP地址:inet_ntop;转换端口:ntohs,sprintf
memset(buf, 0x00, size);
inet_ntop(AF_INET, &addr->sin_addr, buf, size);
snprintf(buf + strlen(buf), size - strlen(buf), "%u", ntohs(addr->sin_port));
}
// 转为⽹络字节序地址结构数据 inet_pton
void sockets::fromIpPort(const char *ip, uint16_t port, struct sockaddr_in *addr)
{
inet_pton(AF_INET, ip, &addr->sin_addr.s_addr);
addr->sin_port = htons(port);
}
// 初始化数据,INADDR_LOOPBACK / INADDR_ANY
InetAddress::InetAddress(uint16_t port)
{
_addr.sin_family = AF_INET;
_addr.sin_addr.s_addr = INADDR_ANY;
_addr.sin_port = htons(port);
}
// 初始化数据
InetAddress::InetAddress(const std::string ip, uint16_t port)
{
_addr.sin_family = AF_INET;
sockets::fromIpPort(ip.c_str(), port, &_addr);
}
// 地址转字符串
std::string InetAddress::toIpPort() const
{
char buf[64] = {0};
sockets::toIpPort(buf, 64, &_addr);
return buf;
}
// 获取地址数据
const struct sockaddr *InetAddress::getSockAddr() const
{
return (struct sockaddr *)&_addr;
}
// 设置地址数据
void InetAddress::setSockAddr(struct sockaddr_in addr)
{
_addr = addr;
}
void Socket::bind(const InetAddress &localaddr)
{
return sockets::bind(_sockfd, localaddr.getSockAddr());
}
void Socket::listen()
{
return sockets::listen(_sockfd);
}
int Socket::accept(InetAddress *peeraddr)
{
struct sockaddr_in addr;
int fd = sockets::accept(_sockfd, &addr);
peeraddr->setSockAddr(addr);
return fd;
}
// IPPROTO_TCP, TCP_NODELAY
void Socket::setTcpNoDelay(bool on)
{
// int setsockopt(int sockfd,int level,int optname,const void* optval,size_t size);
int opt = on ? 1 : 0;
setsockopt(_sockfd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
}
// SOL_SOCKET, SO_REUSEADDR
void Socket::setReuseAddr(bool on)
{
int opt = on ? 1 : 0;
setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
}
// SOL_SOCKET, SO_REUSEPORT
void Socket::setReusePort(bool on)
{
int opt = on ? 1 : 0;
setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
}
// SOL_SOCKET, SO_KEEPALIVE
void Socket::setKeepAlive(bool on)
{
int opt = on ? 1 : 0;
setsockopt(_sockfd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
}
}
**解析:**namespace sockets:封装Linux底层socket系统调用(多Linux原生socket API的C++封装);class InetAddress:对IPv4地址的封装(IP+端口地址类),方便地设置、获取、转换IP和端口,不用每次都处理sockaddr_in结构体;class socket:封装一个socket文件描述符,one socket=one fd,RAII封装(创建/绑定/监听/关闭);RAII机制:构造创建fd,析构自动关闭,永不泄露。
涉及知识点:
(1)::socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC,IPPROTO_TCP);AF_INET:IPv4;SOCK_STREAM:TCP;SOCK_NONBLOCK:非阻塞;SOCK_CLOEXEC:执行关闭;IPPROTO_TCP:TCP协议
(2)accept4:连接客户端新连接,返回新的fd
int accept(int sockfd,struct sockaddr* addr,socklen_t* addrln);
int accept4(int sockfd,struct sockaddr* addr,socklen_t* addrln,int flags);
accept4=accept+fcnt设置成非阻塞+设置CLOEXEC
(3)inet_ntop:把二进制网络地址转换成人类能看懂的IP字符串(0x7F000001="127.0.0.1");inet_pton:把字符串IP转换为二进制网络地址
(4)ntohs:把网络字节序(大端)转成主机字节序(小端)(0x0050=80),打印查看时用;htons:主机端号转换为网络字节序,绑定时用
8.acceptor.h/acceptor.cc
Acceptor 是服务器的 "连接接收器",专门负责监听端口、接收新客户端连接,并将已建立的 socket 转交给 TcpServer 分配给 IO 线程。
acceptor.h
cpp
#pragma once
#include "details.h"
#include "channel.h"
#include "eventloop.h"
#include "socket.h"
namespace net
{
using NewConnectionCallback = std::function<void(int, InetAddress)>;
class Acceptor
{
public:
// 初始化成员,设置channel回调函数,设置套接字的选项标志
Acceptor(EventLoop *loop, const InetAddress &addr);
// 关闭套接字,移除套接字的事件监控
~Acceptor();
void setNewConnectionCallback(NewConnectionCallback cb);
void listen();
private:
void handleRead(Timestamp recvTime);
private:
EventLoop *_loop;
Socket _acceptSocket;
Channel _acceptChannel;
NewConnectionCallback _newConnCallback;
int _idleFd; // 这是一个描述符的占为符
};
} // namespace net
cpp
#include "acceptor.h"
namespace net
{
// 初始化成员,设置channel回调函数,设置套接字的选项标志
Acceptor::Acceptor(EventLoop *loop, const InetAddress &addr)
: _loop(loop),
_acceptSocket(sockets::createNonblockSocket()),
_acceptChannel(_loop, _acceptSocket.fd()),
_idleFd(::open("/dev/null", O_CLOEXEC | O_CREAT))
{
// idleFd占位符的作用:
// 应用于accept的时候,进程描述符数量达到上限,内核里边有就绪的新连接,但是受限于进程描述符数量,无法获取出来,就会一直触发可读事件
assert(_idleFd >= 0);
// 设置套接字选项
_acceptSocket.setReuseAddr(true);
_acceptSocket.bind(addr);
// 为channel设置事件处理回调函数
_acceptChannel.setReadCallback(std::bind(&Acceptor::handleRead, this, std::placeholders::_1));
}
// 关闭描述符,移除套接字的事件监控
Acceptor::~Acceptor()
{
::close(_idleFd);
_acceptChannel.disableAll();
_acceptChannel.remove();
}
void Acceptor::listen()
{
_loop->assertInLoopThread();
// 对监听套接字开始监听
_acceptSocket.listen();
// 对channel启动读事件监控
_acceptChannel.enableReading();
}
void Acceptor::setNewConnectionCallback(NewConnectionCallback cb)
{
_newConnCallback = std::move(cb);
}
void Acceptor::handleRead(Timestamp recvTime)
{
_loop->assertInLoopThread();
InetAddress addr;
int fd = _acceptSocket.accept(&addr);
if (fd >= 0)
{
if (_newConnCallback)
_newConnCallback(fd, addr);
else
::close(fd);
}
else
{
// 出错了,就绪的连接就没取出来,关闭占位符,取出新连接,重新进行占位
::close(_idleFd);
_idleFd = _acceptSocket.accept(&addr);
::close(_idleFd);
_idleFd = open("/dev/null", O_CLOEXEC | O_CREAT);
}
}
}
9.buffer.h/buffer.cc
一个自动扩容,自动管理的字节缓冲区,专门给TCP连接收数据、发数据用的
buffer.h
cpp
#pragma once
/*
封装实现一个用户态的套接字收发缓冲区,用于后边实现的Connection连接对象
*/
#include <vector>
#include <string>
#include <optional>
namespace net
{
class Buffer
{
public:
Buffer();
Buffer(const Buffer &other);
Buffer(Buffer &&other);
Buffer &operator=(const Buffer &other);
Buffer &operator=(Buffer &&other);
void swap(Buffer &other);
// 获取可读起始地址
const char *peek() const;
// 获取可读数据大小
size_t readableBytes() const;
// 获取当前可写空间⼤⼩
size_t writableBytes() const;
// 将读指针向后偏移指定长度
void retrieve(size_t len);
void retrieveAll();
// 获取所有数据到string中,并偏移读索引
std::string retrieveAllAsString();
// 获取指定⻓度数据到string中,并偏移读索引
std::string retrieveAsString(size_t len);
// 判断扩容,向末尾追加数据,偏移写索引 std::copy
void append(const void *data, size_t len);
// 若可写空间⼤⼩⼩于len则扩容
void ensureWritableBytes(size_t len);
// 获取写起始地址
char *beginWrite();
// 偏移写指针
void hasWritten(size_t len);
// 分块读取数据到buffer和临时空间中
// 若实际读取⻓度⼩于等于buffer可写空间⼤⼩,则偏移写索引即可
// 若实际读取⻓度⼤于buffer可写空间⼤⼩,则需要将临时空间的数据追加到buffer中
ssize_t readFd(int fd, int *savedErrno); // 从描述符中读取数据到缓冲区中
// 追加getline&&prependableBytes prepend
size_t prependableBytes();
void prepend(const void *data, size_t len);
std::optional<std::string>getline();
private:
char *begin(); // 获取空间起始地址
const char *begin() const;
// 扩容
void makeSpace(size_t len);
private:
const static int kInitialSize = 1024; // 默热缓冲区大小
const static int kCheapPrepend = 8; // 前置默认预留空间大小
// 使用vector进行空间管理
std::vector<char> _buffer;
size_t _reader_idx; // 读位置索引 描述读取位置
size_t _writer_idx; // 写位置索引 描述写入位置
};
}
cpp
#include "buffer.h"
#include <cassert>
#include <cstring>
#include <sys/uio.h>
#include <iostream>
#include <string>
namespace net
{
Buffer::Buffer()
: _buffer(kInitialSize),
_reader_idx(kCheapPrepend),
_writer_idx(kCheapPrepend) {}
Buffer::Buffer(const Buffer &other)
: _buffer(other._buffer),
_reader_idx(other._reader_idx),
_writer_idx(other._writer_idx) {}
Buffer::Buffer(Buffer &&other)
{
Buffer tmp;
tmp.swap(other);
tmp.swap(*this);
}
Buffer &Buffer::operator=(const Buffer &other)
{
Buffer tmp(other);
tmp.swap(*this);
return *this;
}
Buffer &Buffer::operator=(Buffer &&other)
{
Buffer tmp(std::move(other));
tmp.swap(*this);
return *this;
}
void Buffer::swap(Buffer &other)
{
_buffer.swap(other._buffer);
std::swap(_reader_idx, other._reader_idx);
std::swap(_writer_idx, other._writer_idx);
}
// 获取可读起始地址
const char *Buffer::peek() const
{
return begin() + _reader_idx;
}
// 获取可读数据大小
size_t Buffer::readableBytes() const
{
return _writer_idx - _reader_idx;
}
// 获取当前可写空间⼤⼩
size_t Buffer::writableBytes() const
{
return _buffer.size() - _writer_idx;
}
// 将读指针向后偏移指定长度
void Buffer::retrieve(size_t len)
{
assert(len <= readableBytes());
_reader_idx += len;
}
void Buffer::retrieveAll()
{
retrieve(readableBytes());
}
// 获取所有数据到string中,并偏移读索引
std::string Buffer::retrieveAllAsString()
{
return retrieveAsString(readableBytes());
}
// 获取指定⻓度数据到string中,并偏移读索引
std::string Buffer::retrieveAsString(size_t len)
{
assert(len <= readableBytes());
std::string retval;
// 获取数据到string中
retval.assign(peek(), len);
// 偏移读指针
retrieve(len);
return retval;
}
// 判断扩容,向末尾追加数据,偏移写索引 std::copy
void Buffer::append(const void *data, size_t len)
{
// 1.确保空闲空间足够(不够就需要扩容)
ensureWritableBytes(len);
// 2.将数据拷贝到缓冲区
std::copy((const char *)data, (const char *)data + len, beginWrite());
// 3.将指针向后偏移
hasWritten(len);
// std::cout << "[" << std::string((const char *)data, len) << "]\n";
}
// 若可写空间⼤⼩⼩于len则扩容
void Buffer::ensureWritableBytes(size_t len)
{
// 检查后置空闲空间是否足够,足够则返回,不够则扩容
if (len <= writableBytes())
{
return;
}
else
{
makeSpace(len);
}
}
// 获取写起始地址
char *Buffer::beginWrite()
{
return begin() + _writer_idx;
}
// 偏移写指针
void Buffer::hasWritten(size_t len)
{
assert(len <= writableBytes());
_writer_idx += len;
}
// 分块读取数据到buffer和临时空间中
// 若实际读取⻓度⼩于等于buffer可写空间⼤⼩,则偏移写索引即可
// 若实际读取⻓度⼤于buffer可写空间⼤⼩,则需要将临时空间的数据追加到buffer中
const char *Buffer::begin() const // 获取空间起始地址
{
return &_buffer[0];
}
char *Buffer::begin() // 获取空间起始地址
{
return &_buffer[0];
}
size_t Buffer::prependableBytes()
{
return _reader_idx;
}
// 扩容
void Buffer::makeSpace(size_t len)
{
// 1.前置空闲空间大小+后置空闲空间大小<len+kCheapPrepend
// 这种情况简单处理:向后扩容
// 2.前置空闲空间大小+后置空闲空间大小>=len+kCheapPrepend
// 这种情况,将以前已有的数据,向前拷贝到起始位置,将空闲空间全部放在后便作为连续空间
if (prependableBytes() + writableBytes() < len + kCheapPrepend)
{
_buffer.resize(_writer_idx + len);
}
else
{
assert(kCheapPrepend < _reader_idx);
// 1.获取可读数据大小
size_t dataSize = readableBytes();
// 2.将数据拷贝到起始位置(前置还有一个固定的预留空间)
std::copy(begin() + _reader_idx,
begin() + _writer_idx,
begin() + kCheapPrepend);
// 3.重置读写指针位置
_reader_idx = kCheapPrepend;
_writer_idx = _reader_idx + dataSize;
assert(dataSize == readableBytes());
}
}
void Buffer::prepend(const void *data, size_t len)
{
assert(len <= prependableBytes());
// 将读指针向前偏移len字节
_reader_idx -= len;
// 拷贝数据到读指针指向位置
std::copy((const char *)data, // 要拷贝的目标数据的起始位置
(const char *)data + len, // 要拷贝的目标数据的结束位置
begin() + _reader_idx);
}
std::optional<std::string> Buffer::getline()
{
// memchr 从可读数据中查找\n
void *pos = memchr((void *)peek(), '\n', readableBytes()); // strchr
if (pos == NULL)
{
return std::nullopt;
}
// 构造string对象,拷贝数据进去
size_t lineSize = (char *)pos - peek() + 1;
// std::string str(peek(), lineSize);
// std::cout << "获取数据:[" << str << std::endl;
// 将读指针向后偏移
return retrieveAsString(lineSize);
}
ssize_t Buffer::readFd(int fd, int *savedErrno) // 从描述符中读取数据到缓冲区中
{
// 数据读取采用分块数据读取(大块数据读取,在一次IO中取出所有可读数据)
// ssize_t readv(int fd,const struct iovec*iov,int iovcnt);
// ssize_t read(int fd,void* buf ,size_t count);
size_t wableSize = writableBytes();
char temp[65536]; // 64k
struct iovec vec[2];
vec[0].iov_base = beginWrite(); // 读取的数据优先写入的位置
vec[0].iov_len = wableSize; // 该位置最大写入的数据长度
vec[1].iov_base = temp; // 第二个备用空间
vec[1].iov_len = 65536;
// 1.判断是否需要使用备用空间
int count = wableSize >= 65536 ? 1 : 2;
ssize_t ret = readv(fd, vec, count);
// 返回值是世纪读取到的数据量,判断是否世纪读取的数据大小小于writableBytes()
// ret<=writableBytes()代表第二块备用空间没用上,偏移写指针即可
// ret>writableBytes(),代表备用空间中必然有一部分数据:ret-writableBytes()
// 这种情况下就需要经备用空间的数据追加到缓冲区中
if (ret < 0)
{
*savedErrno = errno;
}
else if (ret <= wableSize)
{
hasWritten(ret); // 将写指针向后偏移
}
else
{
hasWritten(wableSize); // 将写指针直接偏移到末尾
append(temp, ret - wableSize);
}
return ret;
}
}
解析: Buffer 是TCP 连接专用的字节流缓冲区 ,用于非阻塞 IO 场景下缓存收发数据 。 因为 TCP 是流协议,无法保证一次读写完成所有数据,必须用缓冲区暂存未处理完的数据,Buffer 就是为此设计的高性能内存管理器。
涉及知识点:
(1)struct iovec{void * iov_base;//内存起始地址
size_t iov_len;//这块内存多大};
struct iovec=一块"内存地址+长度"的描述块;vec[2]=准备两块内存,让readv一次性把数据填进去
10.connection.h/connection.cc
负责收发数据,管理连接状态,处理事件(相当于一个客户端连接的主权管理者)
connection.h
cpp
#pragma once
#include "channel.h"
#include "details.h"
#include "socket.h"
#include "buffer.h"
#include <any>
#include <cassert>
namespace net
{
class TcpConnection;
using TcpConnectionPtr = std::shared_ptr<TcpConnection>;
using ConnectionCallback = std::function<void(TcpConnectionPtr)>;
using MessageCallback = std::function<void(TcpConnectionPtr, Buffer *, Timestamp)>;
using CloseCallback = std::function<void(TcpConnectionPtr)>;
class TcpConnection : public std::enable_shared_from_this<TcpConnection>
{
enum State
{
kDisconnected,
kConnecting,
kConnected,
kDisconnecting
};
public:
// 初始化成员;设置channel回调;设置连接保活
TcpConnection(EventLoop *loop, int fd, int id) : _id(id),
_state(kConnecting),
_loop(loop),
_socket(new Socket(fd)),
_channel(new Channel(loop, fd))
{
// 设置套接字选项,连接保活
_socket->setKeepAlive(true);
// 设置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_DEBUG("construct TcpConnection at:%p-%d", this, _socket->fd());
}
~TcpConnection()
{
LOG_DEBUG("desstruct TcpConnection at:%p-%d", this, _socket->fd());
assert(_state == kDisconnected);
}
// 成员操作
int id() { return _id; }
bool connected() { return _state == kConnected; }
bool disconnected() { return _state == kDisconnected; }
EventLoop *loop() { return _loop; }
Buffer *inputBuffer() { return &_inputBuffer; }
Buffer *outputBuffer() { return &_outputBuffer; }
void setContext(std::any context) { _context = std::move(context); }
const std::any &context() const { return _context; }
std::any &mutablecontext() { return _context; }
void setConnectionCallback(ConnectionCallback cb) { _onConnection = std::move(cb); }
void setMessageCallback(MessageCallback cb) { _onMessage = std::move(cb); }
void setCloseCallback(CloseCallback cb) { _onClose = std::move(cb); }
void send(const void *data, size_t len);
void forceClose();
void connectEstablished();
void connectDestroyed();
private:
void sendInLoop(const void *data, size_t len);
void sendInLoop(const std::string &str);
void forceCloseInLoop();
void handleRead(Timestamp recvTime);
void handleWrite();
void handleClose();
void handleError();
private:
int64_t _id; // 标识id
State _state; // 连接状态
EventLoop *_loop;
std::unique_ptr<Socket> _socket; // 描述符对应的套接字对象
std::unique_ptr<Channel> _channel; // 描述符事件处理对象
Buffer _inputBuffer; // 输入缓冲区
Buffer _outputBuffer; // 输出缓冲区
std::any _context; // 上下文对象
ConnectionCallback _onConnection;
MessageCallback _onMessage;
CloseCallback _onClose;
};
}
cpp
#include "connection.h"
#include "eventloop.h"
namespace net
{
void TcpConnection::send(const void *data, size_t len)
{
// 若发送数据的时候就在loop线程中,则直接发送,否则则将send任务压入到loop任务池中
if (_loop->isInLoopThread())
{
sendInLoop(data, len);
}
else
{
std::string str((char *)data, len);
void (TcpConnection::*fp)(const std::string &) = &TcpConnection::sendInLoop;
_loop->runInLoop(std::bind(fp, this, str));
}
}
void TcpConnection::sendInLoop(const std::string &str)
{
sendInLoop((const void *)str.c_str(), str.size());
}
void TcpConnection::sendInLoop(const void *data, size_t len)
{
_loop->assertInLoopThread();
// 1.若当前没有进行描述符的写事件监控,且发送缓冲区中没有数据L:则直接write/send数据,否则将数据放入到发送缓冲区中
// 2.若直接发送数据,数据没有发送完,就要把生于数据放入到自己的发送缓冲区,开启写事件监控
bool errFlag = false; // 错误标志,用于保存write是否出错了
int32_t leftSize = len; // 剩余要发送的数据量
int32_t nwrote = 0;
if (!_channel->isWriting() && _outputBuffer.readableBytes() == 0)
{
nwrote = sockets::write(_socket->fd(), data, len);
if (nwrote >= 0)
{
leftSize -= nwrote; // 计算剩余数据
}
else
{
LOG_ERROR("写入数据失败!");
errFlag = true;
}
}
if (leftSize > 0 && errFlag == false)
{
// 将剩余数据放入到缓冲区
_outputBuffer.append((const char *)data + nwrote, leftSize);
if (!_channel->isWriting())
{
_channel->enableWriting(); // 开启写事件监控
}
}
}
void TcpConnection::forceClose()
{
// 连接不管是否在线程中,都不能直接关闭,必须压任务到loop任务池中进行
// 主要是避免任务池中有send任务
_state = kDisconnecting; // 连接状态设置为关闭中
_loop->queueInLoop(std::bind(&TcpConnection::forceCloseInLoop, shared_from_this()));
}
void TcpConnection::forceCloseInLoop()
{
_loop->assertInLoopThread();
// 设置状态为kdisConnected,解除事件监控,移除事件监控,调用回调函数....
if (_state == kConnected || _state == kDisconnecting)
{
handleClose();
}
// kDisconnected状态说明:这是调用foreClose的时候设置的
// kConnected状态说明:整个服务端释放资源的时候会调用connectionCallback-内部可能用户调用cnn-forceClose
}
void TcpConnection::handleClose()
{
// 这是实际的释放资源回调
// 设置状态为kDisconnected,解除事件监控,调用回调函数
_loop->assertInLoopThread();
// 这个断言保证了handClose永远只会被调用一次
assert(_state == kConnected || _state == kDisconnecting);
_state = kDisconnected;
_channel->disableAll();
TcpConnectionPtr gardThis(shared_from_this()); // 额外保存一份智能指针对象,防止
if (_onConnection)
_onConnection(gardThis); // 这是用户设置的连接建立/关闭回调函数
if (_onClose)
_onClose(gardThis); // 这是框架内部的资源释放回调处理函数
}
void TcpConnection::connectEstablished()
{
// 连接对象是所有初始化工作都完成后,才会被调用的接口,而且必须在loop线程中
_loop->assertInLoopThread();
assert(_state == kConnecting);
_state=kConnected;
// 设置channel中的观察者对象
_channel->tie(shared_from_this());
// 启动channel的读事件监控
_channel->enableReading();
// 调用连接建立回调函数
if (_onConnection)
_onConnection(shared_from_this());
}
void TcpConnection::connectDestroyed()
{
// 这个接口是整个服务器TcpServer析构的时候释放连接对象时调用的
_loop->assertInLoopThread();
if (_state == kConnected) // 连接建立中
{
_channel->disableAll();
if (_onConnection)
_onConnection(shared_from_this());
}
_channel->remove(); // 移除事件监控管理
}
void TcpConnection::handleRead(Timestamp recvTime)
{
_loop->assertInLoopThread();
// 将socket内核缓冲区的数据读取到outputbuffer中,然后调用messageCallback
int errNum;
ssize_t n = _inputBuffer.readFd(_socket->fd(), &errNum);
if (n == 0)
{
// 连接断开
return handleClose();
}
else if (n < 0)
{
// 读取出错
return handleError();
}
if (_onMessage)
_onMessage(shared_from_this(), &_inputBuffer, recvTime);
}
void TcpConnection::handleWrite() // 写事件触发的时候调用的
{
_loop->assertInLoopThread();
if (_channel->isWriting() == false)
{
// 连接半关闭
LOG_ERROR("CONNECTION FD %d is shutdown write", _socket->fd());
return;
}
ssize_t n = sockets::write(_socket->fd(), _outputBuffer.peek(), _outputBuffer.readableBytes());
if (n <= 0)
{
LOG_ERROR("发送数据出错");
return;
}
_outputBuffer.retrieve(n); // 将发送缓冲区的读指针向后偏移
// 需要考虑发送缓冲区中的数据有没有发送完毕,发送缓冲区没有数据了,关闭写事件监控
if (_outputBuffer.readableBytes() == 0)
{
_channel->disableWriting();
}
}
void TcpConnection::handleError()
{
_loop->assertInLoopThread();
LOG_ERROR("连接操作出错");
}
}
**解析:**管理fd,管理channel(读写事件),管理接收/发送缓冲区,管理连接状态,向上提供连接回调、消息回调、关闭回调。
涉及知识点:
(1)std::enable_shared_ptr_from_this(#include<memory>):让一个被shared_ptr管理的对象,在类的内部能安全拿到自己的shared_ptr
(2)void (Tcpconnection::*fp)(const std::string &)=&Tcpconnection::sendINLoop;void:返回值;Tcpconnection::*fp:定义一个叫fd的指针,指向Tcpconnection的成员函数;(const std::string &):函数参数列表;&Tcpconnection::sendINLoop:fp指向sendInLoop这个函数;因为要将成员函数传给bind(sendInLoop有两个重载),用fp明确告诉编译器我要的是参数为string的版本
11.server.h/server.cc
整个服务器的总指挥,启动监听,接收新连接,分配IO线程,管理所有连接,回调通知用户
server.h
cpp
#pragma once
#include "eventloop.h"
#include "connection.h"
#include "acceptor.h"
namespace net
{
class TcpServer
{
public:
// 初始化成员,设置acceptor回调函数
TcpServer(EventLoop *loop, const InetAddress &addr);
// 将当前连接管理池中的所有连接关闭释放
~TcpServer();
void setThreadNum(int count);
void setConnectionCallback(ConnectionCallback cb);
void setMessageCallback(MessageCallback cb);
void start(); // 启动服务器
private:
void onNewConnection(int fd, InetAddress addr);
void removeConnection(TcpConnectionPtr conn);
void removeConnectionInLoop(TcpConnectionPtr conn);
private:
EventLoop *_baseloop; // 主reactor
EventLoopThreadPool _pool; // 从属reactor池
Acceptor _acceptor; // 监听套接字管理对象
std::atomic<int64_t> _next_conn_id; // 链接标识
std::unordered_map<int64_t, TcpConnectionPtr> _connections; // 连接管理池
ConnectionCallback _onConnection;
MessageCallback _onMessage;
};
}
cpp
#include "server.h"
namespace net
{
// 初始化成员,设置acceptor回调函数
TcpServer::TcpServer(EventLoop *loop, const InetAddress &addr)
: _baseloop(loop),
_pool(loop),
_acceptor(loop, addr),
_next_conn_id(1)
{
_acceptor.setNewConnectionCallback(std::bind(&TcpServer::onNewConnection, this, std::placeholders::_1, std::placeholders::_2));
}
// 将当前连接管理池中的所有连接关闭释放
TcpServer::~TcpServer()
{
_baseloop->assertInLoopThread();
for (auto &it : _connections)
{
TcpConnectionPtr conn(it.second);
it.second.reset();
conn->loop()->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}
}
void TcpServer::setThreadNum(int count)
{
_pool.setThreadNum(count);
}
void TcpServer::setConnectionCallback(ConnectionCallback cb)
{
_onConnection = std::move(cb);
}
void TcpServer::setMessageCallback(MessageCallback cb)
{
_onMessage = std::move(cb);
}
// 启动服务器
void TcpServer::start()
{
// 1.启动事件循环池
_pool.start();
// 2.开始监听
_baseloop->runInLoop(std::bind(&Acceptor::listen, &_acceptor));
}
void TcpServer::onNewConnection(int fd, InetAddress addr)
{
_baseloop->assertInLoopThread();
// 新连接的处理:
// 1.给新连接分配一个loop事件循环;
EventLoop *ioLoop = _pool.getNextLoop();
// 2.为新连接构造Connection对象;
auto id = _next_conn_id.fetch_add(1);
TcpConnectionPtr conn(new TcpConnection(ioLoop, fd, id));
conn->setConnectionCallback(_onConnection);
conn->setMessageCallback(_onMessage);
conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
// 3.添加conn管理
_connections.insert(std::make_pair(id, conn));
// 4.调用连接就绪接口
ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}
void TcpServer::removeConnection(TcpConnectionPtr conn)
{
_baseloop->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
void TcpServer::removeConnectionInLoop(TcpConnectionPtr conn)
{
_baseloop->assertInLoopThread();
// 从连接管理池中移除连接管理
_connections.erase(conn->id());
// 执行连接销毁操作
conn->loop()->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}
}
12.client.h/client.cc
客户端总控制器:主动连接服务器,创建Tcpconnection,管理连接生命周期,给用户提供连接/消息回调
client.h
cpp
#pragma once
#include "eventloop.h"
#include "connection.h"
namespace net
{
class TcpClient
{
public:
// 初始化成员
TcpClient(EventLoop *loop, InetAddress addr);
// 关闭连接
~TcpClient();
// 连接服务端
void connect();
// 获取通信连接对象
TcpConnectionPtr connection();
void setConnectionCallback(ConnectionCallback cb);
void setMessageCallback(MessageCallback cb);
private:
// 连接重试接口
void retry(int fd);
void retryInLoop();
// 连接成功时,为描述符构造通信连接对象
void newConnection(int fd);
// 连接关闭时,调用的回调函数,移除连接管理
void removeConnection(TcpConnectionPtr conn);
private:
std::mutex _mutex;
std::condition_variable _cond;
InetAddress _srvAddr;
EventLoop *_baseloop;
TcpConnectionPtr _connection;
ConnectionCallback _onConnection;
MessageCallback _onMessage;
};
} // namespace net
cpp
#include "client.h"
namespace net
{
// 初始化成员
TcpClient::TcpClient(EventLoop *loop, InetAddress addr)
: _srvAddr(addr),
_baseloop(loop) {}
// 关闭连接
TcpClient::~TcpClient()
{
if (_connection)
{
_connection->forceClose();
}
}
void TcpClient::setConnectionCallback(ConnectionCallback cb)
{
_onConnection = std::move(cb);
}
void TcpClient::setMessageCallback(MessageCallback cb)
{
_onMessage = std::move(cb);
}
// 连接服务端:创建套接字,连接服务器(成功则构造connection对象,否则错误)
void TcpClient::connect()
{
int sockfd = sockets::createBlockSocket();
int ret = sockets::connect(sockfd, _srvAddr.getSockAddr());
int errNo = ret == 0 ? 0 : errno;
switch (errNo)
{
case 0: // 连接成功
case EINTR:
case EISCONN: // 套接字已经连接成功
case EINPROGRESS: // 套接字非阻塞的情况下连接服务器,返回正在连接中,需要监控可写事件,描述符可写了才代表连接成功
_baseloop->runInLoop(std::bind(&TcpClient::newConnection, this, sockfd));
break;
case EAGAIN: // 资源不足,连接立即返回无法确定是否连接成功
case EADDRINUSE: // 当前连接绑定的地址被占用
case EADDRNOTAVAIL: // 地址信息无效,通常在绑定地址内核会从空闲端口池绑定地址,但是端口池无空闲端口
case ECONNREFUSED: // 连接被拒绝,通常指服务端没有对连接端口进行监听
case ENETUNREACH: // 网络不可达,通常指网络断开
case ETIMEDOUT: // 网络连接超时
retry(sockfd);
break;
case EACCES: // 权限错误
case EPROTOTYPE: // 套接字类型错误
case ENOTSOCK: // 描述符不是一个套接字描述符
case EFAULT: // 用户空间地址不够用了
case EBADF: // 文件描述符损坏
case EALREADY: // 非阻塞套接字上已有连接
case EAFNOSUPPORT: // 不支持的地址域
default:
LOG_FATAL("连接服务器失败");
break;
}
}
// 获取通信连接对象:连接对象还没有初始化完毕就阻塞等待
TcpConnectionPtr TcpClient::connection()
{
std::unique_lock<std::mutex> lock(_mutex);
_cond.wait(lock, [this]
{ return (bool)_connection; });
return _connection;
}
// 连接重试接口
void TcpClient::retry(int fd)
{
sockets::close(fd);
_baseloop->runAfter(3,std::bind(&TcpClient::retryInLoop,this));
}
void TcpClient::retryInLoop()
{
_baseloop->assertInLoopThread();
connect();//调用connect连接服务器
}
// 连接成功时,为描述符构造通信连接对象
void TcpClient::newConnection(int fd)
{
_baseloop->assertInLoopThread();
TcpConnectionPtr conn(new TcpConnection(_baseloop, fd, 0));
conn->setConnectionCallback(_onConnection);
conn->setMessageCallback(_onMessage);
conn->setCloseCallback(std::bind(&TcpClient::removeConnection, this, std::placeholders::_1));
conn->connectEstablished();
{
std::unique_lock<std::mutex> lock(_mutex);
_connection=conn;
_cond.notify_all();
}
}
// 连接关闭时,调用的回调函数,移除连接管理
void TcpClient::removeConnection(TcpConnectionPtr conn)
{
_baseloop->assertInLoopThread();
{
std::unique_lock<std::mutex> lock(_mutex);
_connection.reset();
}
conn->connectDestroyed();
}
}
四、框架使用示例
基于上述框架实现一个简单的 Echo 服务器:
1.makefile
bash
SRC += $(wildcard ../net/*.cc)
all:client main
client: client.cc $(SRC)
g++ -g -std=c++17 $^ -o $@ -pthread
main: main.cc $(SRC)
g++ -g -std=c++17 $^ -o $@ -pthread
2.client.cc
cpp
#include "../net/eventloop.h"
#include "../net/client.h"
#include <iostream>
void onConnection(net::TcpConnectionPtr conn) {
if (conn->connected()) {
// LOG_DEBUG("连接建立");
}else {
// LOG_DEBUG("连接关闭");
}
}
void onMessage(net::TcpConnectionPtr conn, net::Buffer* buf, net::Timestamp rtime) {
std::string str = buf->retrieveAllAsString();
std::cout << str << std::endl;
}
int main()
{
net::EventLoopThread loopthread;
{
net::TcpClient client(loopthread.startLoop(), net::InetAddress("127.0.0.1", 8080));
client.connect();
client.setConnectionCallback(onConnection);
client.setMessageCallback(onMessage);
auto connection = client.connection();
for (int i = 0; i < 10; i++) {
std::string str = "Hello World + " + std::to_string(i);
connection->send(str.data(), str.size());
}
sleep(10);
}
sleep(10);
return 0;
}
3.main.cc
cpp
#include "../net/poller.h"
#include "../net/channel.h"
#include "../net/eventloop.h"
#include "../net/timestamp.h"
#include "../net/timer.h"
#include "../net/buffer.h"
#include "../net/socket.h"
#include "../net/acceptor.h"
#include "../net/connection.h"
#include "../net/server.h"
#include <iostream>
#include <cassert>
#include <cstring>
void onConnection(net::TcpConnectionPtr conn) {
if (conn->connected()) {
// LOG_DEBUG("连接建立");
}else {
// LOG_DEBUG("连接关闭");
}
}
void onMessage(net::TcpConnectionPtr conn, net::Buffer* buf, net::Timestamp rtime) {
std::string str = buf->retrieveAllAsString();
// std::cout << str << std::endl;
conn->send(str.data(), str.size());
conn->forceClose();
}
int main()
{
net::EventLoop baseloop;
net::TcpServer server(&baseloop, net::InetAddress(8080));
server.setThreadNum(3);
server.setConnectionCallback(onConnection);
server.setMessageCallback(onMessage);
server.start();
baseloop.loop(); //启动主事件循环
// net::Buffer buffer;
// // 添加1000条 HelloWorld+N\n
// uint32_t size = 0;
// for (int i = 0; i < 1000; i++) {
// std::string str = "HelloWorld+" + std::to_string(i) + "\n";
// buffer.append(str.c_str(), str.size());
// size += str.size();
// }
// buffer.prepend(&size, sizeof(size)); //添加前置4B数据
// assert(buffer.readableBytes() == sizeof(size) + size);
// uint32_t num;
// memcpy(&num, buffer.peek(), sizeof(num)); //取出前4字节数据
// assert(num == size);
// buffer.retrieve(sizeof(num));
// for (int i = 0; i < 1000; i++) {
// std::optional<std::string> line = buffer.getline();
// std::string str = "HelloWorld+" + std::to_string(i) + "\n";
// assert(line);
// // std::cout << "[" << str << "] <> [" << *line << "]\n";
// assert(*line == str);
// }
// assert(buffer.readableBytes() == 0);
// // net::EventLoop baseloop;
// // net::Timestamp now = net::Timestamp::now();
// // baseloop.runAt(net::addTime(now, 2), [](){
// // std::cout << "这是一个2s后的定点定时任务\n";
// // });
// // baseloop.runAfter(4, [](){
// // std::cout << "这是一个4s后的延迟定时任务\n";
// // });
// // auto tid = baseloop.runEvery(1, [](){
// // static int count = 0;
// // std::cout << "这是一个1s后的循环定时任务\n";
// // });
// // baseloop.loop();
return 0;
}
4.结果串联
(1)客户端发起连接
Tcpclient--->Socket--->connect--->Connection--->Channel--->EventLoop--->Buffer
(2)服务器接收数据
EventLoop--->Poller--->Acceptor--->Server--->Connection--->Channel--->Buffer
(3)数据流动
read--->Buffer--->用户回调
用户数据--->Buffer--->write
五、框架性能优化点
- epoll ET 模式:边缘触发减少 epoll_wait 的调用次数,结合非阻塞 IO 避免数据读取不完整;
- 内存池 / 预分配:EventList 初始预分配空间,避免频繁扩容;
- 跨线程唤醒优化:使用 eventfd 替代 pipe,减少系统调用开销;
- 无锁化设计:EventLoop 仅在单线程运行,避免多线程竞争;
- 智能指针管理连接:TcpConnection 使用 shared_ptr 管理生命周期,避免野指针。
六、总结与扩展
本文复刻的 muduo 核心框架完整实现了 Reactor 模型的核心逻辑,涵盖了事件循环、多路复用、事件分发、连接管理等关键模块。相比原生 muduo,本框架做了适度简化,但保留了核心设计思想:
- 事件驱动:基于 epoll 的 IO 多路复用,最大化利用内核性能;
- 单线程事件循环:避免多线程竞争,简化同步逻辑;
- 回调式编程:解耦网络层和业务层,提升框架扩展性。
扩展方向
- 多 Reactor 模式:新增 EventLoopThreadPool,主 Reactor 处理连接,子 Reactor 处理 IO;
- 定时器功能:基于 timerfd 实现定时任务;
- 日志系统:完善日志输出,支持不同级别、不同输出端;
- SSL/TLS 支持:整合 OpenSSL 实现安全通信;
- 性能监控:添加统计指标(连接数、QPS、延迟等)。
通过复刻 muduo 库,我们不仅深入理解了 Reactor 模型的落地实现,也掌握了 Linux 高性能网络编程的核心技巧。该框架可作为高性能网络服务的基础,适配游戏服务器、网关、RPC 框架等多种场景。