手写 muduo 库:基于 Reactor 模型打造高性能网络通信框架

在高性能网络编程领域,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, &lt);
            // 组织子字符串
            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; // 取消任务池
    };
}

timer.cc

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;
    };
}

poller.cc

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;    // 错误事件回调
    };
}

channel.cc

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

eventloop.cc

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;
    };
}

socket.cc

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

acceptor.cc

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; // 写位置索引 描述写入位置
    };
}

buffer.cc

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;
    };
}

connection.cc

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;
    };
}

server.cc

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

client.cc

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

五、框架性能优化点

  1. epoll ET 模式:边缘触发减少 epoll_wait 的调用次数,结合非阻塞 IO 避免数据读取不完整;
  2. 内存池 / 预分配:EventList 初始预分配空间,避免频繁扩容;
  3. 跨线程唤醒优化:使用 eventfd 替代 pipe,减少系统调用开销;
  4. 无锁化设计:EventLoop 仅在单线程运行,避免多线程竞争;
  5. 智能指针管理连接:TcpConnection 使用 shared_ptr 管理生命周期,避免野指针。

六、总结与扩展

本文复刻的 muduo 核心框架完整实现了 Reactor 模型的核心逻辑,涵盖了事件循环、多路复用、事件分发、连接管理等关键模块。相比原生 muduo,本框架做了适度简化,但保留了核心设计思想:

  • 事件驱动:基于 epoll 的 IO 多路复用,最大化利用内核性能;
  • 单线程事件循环:避免多线程竞争,简化同步逻辑;
  • 回调式编程:解耦网络层和业务层,提升框架扩展性。

扩展方向

  1. 多 Reactor 模式:新增 EventLoopThreadPool,主 Reactor 处理连接,子 Reactor 处理 IO;
  2. 定时器功能:基于 timerfd 实现定时任务;
  3. 日志系统:完善日志输出,支持不同级别、不同输出端;
  4. SSL/TLS 支持:整合 OpenSSL 实现安全通信;
  5. 性能监控:添加统计指标(连接数、QPS、延迟等)。

通过复刻 muduo 库,我们不仅深入理解了 Reactor 模型的落地实现,也掌握了 Linux 高性能网络编程的核心技巧。该框架可作为高性能网络服务的基础,适配游戏服务器、网关、RPC 框架等多种场景。

相关推荐
小陶来咯20 小时前
图生图 (Image-to-Image) :Python 和 C++ 两种实现方式
java·c++·python
是星辰吖~20 小时前
C++_vector_调用及模拟实现
开发语言·c++
2401_8920709820 小时前
【高性能网络核心】Reactor 模型原理与整体架构:从入门到架构师级理解
网络编程·reactor·epoll·反应堆模型
Ring__Rain20 小时前
nnpp处理,线程
数据结构·c++·算法
兵哥工控20 小时前
MFC 动态数组CArray类使用说明实例
c++·mfc
Cx330❀20 小时前
【Linux网络】从以太网碰撞到 Socket 套接字与网络字节序的深度解析
xml·linux·运维·服务器·开发语言·网络·c++
蜡笔小马20 小时前
15.C++设计模式-观察者模式
c++·观察者模式·设计模式
拂拉氏1 天前
【项目分享-知识讲解】C++标准库string类的模拟实现+KMP算法讲解+哈希思想了解
开发语言·c++·算法·kmp算法·哈希·string类
枕星而眠1 天前
C++ 类与对象核心知识点及面试高频题详解
开发语言·c++·面试
小poop1 天前
string 类从入门到深入
c++