图床项目实现:Muduo 网络框架学习以及登录注册功能实现

Muduo 网络框架深度解析:从 EPoll 到 HTTP Server

1. 概述

Muduo 是陈硕开发的一款基于 Reactor 模式的多线程网络库,采用 C++11 编写,核心代码不到 3000 行。本文结合实际 HTTP Server 代码,深入剖析其架构设计。


2. 整体架构设计

2.1 模块层次

复制代码
+------------------------------------------+
|            Application Layer              |
|  CHttpConn  |  ApiLogin  |  ApiRegister   |
+------------------------------------------+
|          TcpConnection (业务逻辑)         |
|   OnMessage  |  OnConnection  |  send()  |
+------------------------------------------+
|          TcpServer (连接管理)              |
|   acceptor  |  connectionCallback         |
+------------------------------------------+
|         EventLoop (事件循环)               |
|            loop() 循环                    |
+------------------------------------------+
|         Channel (事件分发)                 |
|       fd  |  events  |  handleEvent       |
+------------------------------------------+
|         EpollPoller (Epoll 封装)          |
|         epoll_wait()  |  epoll_ctl       |
+------------------------------------------+
|            Socket (fd 管理)               |
+------------------------------------------+

2.2 请求处理流程

复制代码
Client                                         Server
  |                                               |
  |---------------- TCP Connect ----------------->|
  |                                               |
  |                  mainReactor                  |
  |                  (acceptor)                   |
  |                       |                       |
  |                       v                       |
  |              +----------------+               |
  |              |  epoll_accept  |               |
  |              +----------------+               |
  |                       |                       |
  |                       v                       |
  |              分配到某个 subReactor            |
  |              (round-robin)                    |
  |                       |                       |
  |              +----------------+               |
  |              |  channel:fd |               |
  |              |  readable   |               |
  |              +----------------+               |
  |                       |                       |
  |                       v                       |
  |              处理 HTTP 请求                   |
  |              JSON 解析                       |
  |              业务逻辑                         |
  |              生成响应                         |
  |                       |                       |
  |                       v                       |
  |<--------------- HTTP Response --------------|
  |                                               |
  |---------------- TCP Close ------------------>|

2.3 关键设计思想

Muduo 的核心设计哲学是区分 IO 线程和业务线程

  • IO 线程:负责 fd 的可读可写事件监听和分发
  • 业务线程池:负责处理业务逻辑(计算任务)

这种设计的优势在于:IO 操作和业务计算分离,避免业务逻辑阻塞 IO 线程。


3. 核心组件解析

3.1 EventLoop - 事件循环引擎

EventLoop 是整个框架的心脏,每个 EventLoop 绑定一个线程,持续调用 epoll_wait 监听事件。

复制代码
class EventLoop {
private:
    EpollPoller* poller_;               // epoll 封装
    std::vector<Channel*> active_channels_;  // 就绪的 channel 列表
    std::atomic<bool> quit_;            // 退出标志
    int wakeup_fd_;                     // 用于唤醒 loop
    Channel wakeup_channel_;            // wakeup fd 对应的 channel
    std::vector<Functor> pending_functors_;  // 待执行的回调

public:
    EventLoop() {
        poller_ = new EpollPoller();

        // 创建 eventfd,用于唤醒 epoll_wait
        // 当其他线程想向 EventLoop 投递任务时,向 wakeup_fd 写一个字节即可唤醒
        wakeup_fd_ = ::eventfd(0, EFD_NONBLOCK);
        wakeup_channel_.setFd(wakeup_fd_);
        wakeup_channel_.setReadCallback([this]() { handleWakeup(); });
        wakeup_channel_.enableReading();
    }

    // ==========================================
    // 核心循环:每次调用 epoll_wait 等待事件
    // ==========================================
    void loop() {
        while (!quit_) {
            // 第一步:清空上一轮的就绪列表
            active_channels_.clear();

            // 第二步:调用 epoll_wait 等待事件
            // timeout = 10000ms,超时后即使没有事件也会返回
            // 这样可以定期处理 pending_functors_,避免饥饿
            int num_events = poller_->epoll_wait(&active_channels_, 10000);

            // 第三步:遍历所有就绪的 channel,触发回调
            for (int i = 0; i < num_events; ++i) {
                active_channels_[i]->handleEvent();
            }

            // 第四步:执行其他线程投递的任务
            // 这步很关键!实现了跨线程任务投递
            doPendingFunctors();
        }
    }

    // ==========================================
    // 在 IO 线程执行任务(线程安全)
    // 如果在当前线程,直接执行
    // 如果在其他线程,投递到 pending_functors_
    // ==========================================
    void runInLoop(const Functor& cb) {
        if (isInLoopThread()) {
            cb();  // 本线程,直接执行
        } else {
            {
                std::lock_guard<std::mutex> lock(mutex_);
                pending_functors_.push_back(cb);
            }
            wakeup();  // 唤醒 loop 处理任务
        }
    }

    // ==========================================
    // 更新 channel 到 epoll
    // 由 Channel 调用,当 enableReading/enableWriting 时触发
    // ==========================================
    void updateChannel(Channel* ch) {
        poller_->updateChannel(ch);
    }

    // 退出 loop
    void quit() { quit_ = true; wakeup(); }

private:
    // 处理 wakeup fd 的读事件(其他线程投递任务时触发)
    void handleWakeup() {
        uint64_t one;
        ::read(wakeup_fd_, &one, sizeof(one));  // 消费掉数据
        // 此时 pending_functors_ 已有任务,doPendingFunctors 会执行
    }

    // 执行 pending_functors_ 中的任务
    void doPendingFunctors() {
        std::vector<Functor> functors;
        {
            std::lock_guard<std::mutex> lock(mutex_);
            functors.swap(pending_functors_);  // 交换出去,减少锁持有时间
        }
        for (auto& f : functors) {
            f();  // 执行任务
        }
    }
};

关键点

  1. EventLoop 绑定一个线程,不能复制(noncopyable)
  2. epoll_wait 返回就绪的 Channel 列表
  3. runInLoop 保证线程安全,支持跨线程调用
  4. pending_functors_ 实现其他线程向 IO 线程投递任务
  5. wakeup_fd_ 用 eventfd 实现高效的线程唤醒

3.2 EpollPoller - EPoll 封装

Poller 负责管理所有 Channel 的注册和事件监听,对上层透明。

复制代码
class EpollPoller {
private:
    int epoll_fd_;                             // epoll 实例 fd
    std::vector<struct epoll_event> events_;  // 事件数组
    std::map<int, Channel*> channels_;        // fd -> Channel* 映射

public:
    // 构造:创建 epoll 实例
    // epoll_create 参数在新版本被忽略,但必须 > 0
    EpollPoller() {
        epoll_fd_ = ::epoll_create(5);
        events_.resize(16);  // 初始容量 16
    }

    // ==========================================
    // 更新 channel(注册或修改)
    // 第一步:判断是新增还是修改
    // 第二步:构造 epoll_event(设置关注的事件)
    // 第三步:调用 epoll_ctl
    // ==========================================
    void updateChannel(Channel* ch) {
        int fd = ch->fd();

        // 判断操作类型:ADD 或 MOD
        int op;
        if (channels_.count(fd) == 0) {
            op = EPOLL_CTL_ADD;  // 新增
        } else {
            op = EPOLL_CTL_MOD;   // 修改
        }

        // 构造 epoll_event
        // data.ptr 指向 Channel,事件就绪时可通过它找到对应的 Channel
        struct epoll_event event;
        event.events = ch->events();  // EPOLLIN | EPOLLOUT | EPOLLET
        event.data.ptr = ch;

        // 调用 epoll_ctl
        ::epoll_ctl(epoll_fd_, op, fd, &event);
        channels_[fd] = ch;
    }

    // ==========================================
    // 移除 channel
    // 直接调用 epoll_ctl DEL,然后从 map 中删除
    // ==========================================
    void removeChannel(Channel* ch) {
        int fd = ch->fd();
        ::epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr);
        channels_.erase(fd);
    }

    // ==========================================
    // 等待事件
    // 返回就绪的 Channel 列表
    // ==========================================
    int epoll_wait(std::vector<Channel*>* active_channels, int timeout_ms) {
        int num_events = ::epoll_wait(
            epoll_fd_,
            events_.data(),
            events_.size(),
            timeout_ms
        );

        // 遍历就绪的事件,填充 active_channels
        for (int i = 0; i < num_events; ++i) {
            // 从 data.ptr 取出 Channel*
            Channel* ch = static_cast<Channel*>(events_[i].data.ptr);
            // 设置 Channel 的 revents(就绪事件)
            ch->set_revents(events_[i].events);
            active_channels->push_back(ch);
        }

        // 如果事件数组满了,自动扩容(避免下轮 epoll_wait 截断)
        if (num_events == static_cast<int>(events_.size())) {
            events_.resize(events_.size() * 2);
        }

        return num_events;
    }
};

关键点

  1. channels_ 维护 fd -> Channel 映射,支持 O(1) 查找
  2. updateChannel 自动判断是 ADD 还是 MOD
  3. 事件数组满后自动扩容,避免截断
  4. data.ptr 指向 Channel,实现事件->对象的转换

3.3 Channel - 事件分发器

Channel 是事件处理的中间层,将 fd 和其关联的回调函数绑定在一起。

复制代码
class Channel {
public:
    typedef std::function<void()> EventCallback;

private:
    // 事件标志位
    static const int kNoneEvent = 0;
    static const int kReadEvent = EPOLLIN | EPOLLPRI;  // 可读 + 紧急数据
    static const int kWriteEvent = EPOLLOUT;           // 可写

    EventLoop* loop_;           // 所属 EventLoop
    const int fd_;              // 文件描述符
    int events_;                // 注册的事件(要关注什么)
    int revents_;               // 就绪的事件(发生了什么)
    bool event_handling_;       // 是否正在处理事件

    // 回调函数(在 TcpConnection 中设置)
    EventCallback read_callback_;
    EventCallback write_callback_;
    EventCallback close_callback_;
    EventCallback error_callback_;

public:
    Channel(EventLoop* loop, int fd)
        : loop_(loop), fd_(fd), events_(kNoneEvent) {}

    // ==========================================
    // 设置回调函数
    // 这些回调在 handleEvent 中被调用
    // ==========================================
    void setReadCallback(const EventCallback& cb) { read_callback_ = cb; }
    void setWriteCallback(const EventCallback& cb) { write_callback_ = cb; }
    void setCloseCallback(const EventCallback& cb) { close_callback_ = cb; }
    void setErrorCallback(const EventCallback& cb) { error_callback_ = cb; }

    // ==========================================
    // 启用事件监听
    // enableReading:关注 EPOLLIN,fd 可读时触发 read_callback_
    // enableWriting:关注 EPOLLOUT,fd 可写时触发 write_callback_
    // 每次修改 events 后,需要调用 updateChannel 更新到 epoll
    // ==========================================
    void enableReading() {
        events_ |= kReadEvent;
        loop_->updateChannel(this);  // 更新到 epoll
    }

    void enableWriting() {
        events_ |= kWriteEvent;
        loop_->updateChannel(this);
    }

    void disableAll() {
        events_ = kNoneEvent;
        loop_->updateChannel(this);
    }

    void disableWriting() {
        events_ &= ~kWriteEvent;
        loop_->updateChannel(this);
    }

    // ==========================================
    // 事件处理分发
    // 根据 revents_ 判断发生了什么事件
    // 然后调用对应的回调函数
    // ==========================================
    void handleEvent() {
        event_handling_ = true;

        // 第一步:对方关闭(EPOLLHUP)
        // EPOLLHUP 表示对方关闭了写端,但可能还在读
        // 如果同时没有 EPOLLIN,说明连接彻底断了
        if (revents_ & EPOLLHUP && !(revents_ & EPOLLIN)) {
            if (close_callback_) close_callback_();
        }

        // 第二步:可读事件
        // EPOLLIN:普通数据可读
        // EPOLLPRI:带外数据可读
        // EPOLLRDHUP:对方关闭了写端(对端关闭)
        if (revents_ & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)) {
            if (read_callback_) read_callback_();
        }

        // 第三步:可写事件
        if (revents_ & EPOLLOUT) {
            if (write_callback_) write_callback_();
        }

        // 第四步:错误事件
        if (revents_ & EPOLLERR) {
            if (error_callback_) error_callback_();
        }

        event_handling_ = false;
    }

    // getters
    int fd() const { return fd_; }
    int events() const { return events_; }
    void set_revents(int r) { revents_ = r; }
};

关键点

  1. Channel 不拥有 fd,只负责事件分发
  2. enableReading/enableWriting 修改 events_ 后调用 updateChannel
  3. handleEvent 根据 revents_ 分发到不同回调
  4. 回调函数在 TcpConnection 中设置

4. HTTP Server 构建流程

4.1 main 函数入口

复制代码
int main() {
    // ==========================================
    // 步骤 1:创建主事件循环(mainReactor)
    // 主事件循环负责监听 accept 事件
    // ==========================================
    EventLoop loop;

    // ==========================================
    // 步骤 2:配置监听地址和端口
    // ip = "0.0.0.0" 表示监听所有网卡
    // port = 8081
    // ==========================================
    uint16_t port = 8081;
    const char* ip = "0.0.0.0";
    InetAddress addr(ip, port);

    // ==========================================
    // 步骤 3:创建 HTTP 服务器
    // num_event_loops = 4 表示开启 4 个 subReactor
    // subReactor 负责处理 IO 事件
    // ==========================================
    int num_event_loops = 4;
    HttpServer server(&loop, addr, "HttpServer", num_event_loops);

    // ==========================================
    // 步骤 4:启动服务器
    // 内部会创建 acceptor,开始监听连接
    // ==========================================
    server.start();

    // ==========================================
    // 步骤 5:进入事件循环(阻塞)
    // mainReactor 开始 loop,持续处理事件
    // ==========================================
    loop.loop();

    return 0;
}

4.2 HttpServer 封装

复制代码
class HttpServer {
private:
    TcpServer server_;                                  // TcpServer
    std::map<uint32_t, CHttpConnPtr> http_conn_map_;    // 连接映射表
    std::atomic<uint32_t> conn_uuid_generator_ = 0;    // uuid 生成器

public:
    // ==========================================
    // 构造函数
    // 参数:
    //   loop:主事件循环指针
    //   addr:监听地址
    //   name:服务名称
    //   num_event_loops:subReactor 数量
    // ==========================================
    HttpServer(EventLoop* loop, const InetAddress& addr,
               const std::string& name, int num_event_loops) {
        // 设置新连接回调
        // 当客户端 connect() 成功时触发
        server_.setConnectionCallback(
            [this](const TcpConnectionPtr& conn) { onConnection(conn); }
        );

        // 设置消息回调
        // 当收到客户端数据时触发
        server_.setMessageCallback(
            [this](const TcpConnectionPtr& conn, Buffer* buf, Timestamp t) {
                onMessage(conn, buf, t);
            }
        );

        // 设置写完成回调
        // 当数据全部发送完毕时触发
        server_.setWriteCompleteCallback(
            [this](const TcpConnectionPtr& conn) { onWriteComplete(conn); }
        );

        // 设置 subReactor 数量
        // TcpServer 会创建 num_event_loops 个 EventLoop 线程
        server_.setThreadNum(num_event_loops);
    }

    void start() {
        server_.start();
    }

private:
    // ==========================================
    // onConnection:新连接到来或断开
    // 参数:conn - TcpConnection 智能指针
    // ==========================================
    void onConnection(const TcpConnectionPtr& conn) {
        if (conn->connected()) {
            // ========== 连接建立 ==========
            // 第一步:生成唯一标识 uuid
            uint32_t uuid = conn_uuid_generator_++;

            // 第二步:设置上下文
            // std::any 可以存储任意类型
            // 这里存储 uint32_t uuid,用于后续识别连接
            conn->setContext(uuid);

            // 第三步:创建 CHttpConn 并加入映射表
            CHttpConnPtr http_conn = std::make_shared<CHttpConn>(conn);
            http_conn_map_[uuid] = http_conn;

        } else {
            // ========== 连接断开 ==========
            // 第一步:从上下文获取 uuid
            uint32_t uuid = std::any_cast<uint32_t>(conn->getContext());

            // 第二步:从映射表移除
            // CHttpConn 的 shared_ptr 引用计数减 1
            // 当没有其他引用时,CHttpConn 会被析构
            http_conn_map_.erase(uuid);
        }
    }

    // ==========================================
    // onMessage:收到客户端数据
    // 参数:
    //   conn - TcpConnection 智能指针
    //   buf - 数据缓冲区
    //   time - 时间戳
    // ==========================================
    void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) {
        // 第一步:从上下文获取 uuid
        uint32_t uuid = std::any_cast<uint32_t>(conn->getContext());

        // 第二步:从映射表找到对应的 CHttpConn
        CHttpConnPtr& http_conn = http_conn_map_[uuid];

        // 第三步:调用 CHttpConn 的 OnRead 处理请求
        http_conn->OnRead(buf);
    }

    // ==========================================
    // onWriteComplete:数据发送完成
    // ==========================================
    void onWriteComplete(const TcpConnectionPtr& conn) {
        // 可以记录日志或处理完成回调
        // 例如:更新任务状态、触发下一个任务等
    }
};

4.3 std::any 上下文传递

Muduo 使用 std::any 作为 TcpConnection 的上下文容器,支持任意类型:

复制代码
// std::any 是 C++17 的类型擦除容器
// 可以存储任意类型的值,通过 any_cast 提取

// ==========================================
// 设置上下文
// 在 onConnection(连接建立时)调用
// ==========================================
conn->setContext(uint32_t(uuid));

// ==========================================
// 获取上下文
// 在 onMessage(收到消息时)调用
// ==========================================
uint32_t uuid = std::any_cast<uint32_t>(conn->getContext());

// ==========================================
// 安全检查版本
// 如果类型不匹配,std::any_cast 会抛出异常
// ==========================================
if (conn->getContext().has_value()) {
    auto uuid = std::any_cast<uint32_t>(conn->getContext());
}

// ==========================================
// 示例:传递多种类型
// ==========================================
conn->setContext(42);                    // int
conn->setContext(std::string("hello"));   // std::string
conn->setContext(CustomStruct{1, 2});     // 自定义结构体

// 获取时需要知道具体类型
auto val = std::any_cast<int>(conn->getContext());

5. 多线程 Reactor 模型

5.1 架构图

复制代码
                        +------------------+
                        |    Client 1       |
                        |    Client 2       |
                        |    Client 3       |
                        +------------------+
                                |
                                v
                        +------------------+
                        |   mainReactor     |
                        |   (acceptor)      |
                        |   listen_fd=3     |
                        +------------------+
                                |
          +---------------------+---------------------+
          |                     |                     |
          v                     v                     v
+----------------+    +----------------+    +----------------+
|   subReactor0  |    |   subReactor1  |    |   subReactor2  |
|   EventLoop0   |    |   EventLoop1   |    |   EventLoop2   |
|   epoll fd=4   |    |   epoll fd=5   |    |   epoll fd=6   |
|   conn_fd=7    |    |   conn_fd=8    |    |   conn_fd=9    |
|   conn_fd=10   |    |   conn_fd=11   |    |               |
+----------------+    +----------------+    +----------------+
          |                     |                     |
          v                     v                     v
    +----------+          +----------+          +----------+
    |  Task 1  |          |  Task 2  |          |  Task 3  |
    |  计算任务 |          |  计算任务 |          |  计算任务 |
    +----------+          +----------+          +----------+

5.2 One Loop Per Thread 模型

Muduo 的 TcpServer 默认采用 One Loop Per Thread 模式:

  1. mainReactor:只有 1 个,负责 accept 新连接

  2. subReactor:N 个,每个绑定一个 EventLoop 线程,负责 IO 监听

  3. 连接分发:新连接按 round-robin 分发给各个 subReactor

    TcpServer::setThreadNum(4) 的效果:

    mainReactor: listen socket, accept 新连接
    |
    v (round-robin)
    +-----+-----+-----+-----+
    sub0 sub1 sub2 sub3 (4 个 EventLoop 线程)
    | | |
    v v v
    处理各自的 conn_fd

为什么用 round-robin?

  • 保证连接均匀分布到各个 EventLoop
  • 每个 EventLoop 线程只处理自己管理的连接
  • 避免单个 EventLoop 过度负载

5.3 TcpServer 内部逻辑

复制代码
class TcpServer {
private:
    EventLoop* main_loop_;              // 主事件循环
    std::vector<EventLoop*> sub_loops_; // 子事件循环列表
    int next_loop_index_ = 0;          // round-robin 索引
    TcpServerAcceptor* acceptor_;       // 接受器

public:
    // ==========================================
    // setThreadNum:设置 subReactor 数量
    // 内部会创建对应数量的线程,每个线程一个 EventLoop
    // ==========================================
    void setThreadNum(int num_threads) {
        num_threads_ = num_threads;

        // 如果已经启动,需要创建额外的 EventLoop 线程
        if (running_) {
            // 创建并启动 sub EventLoop 线程
            for (int i = 0; i < num_threads_; ++i) {
                std::thread t([this]() {
                    EventLoop* loop = new EventLoop();
                    sub_loops_.push_back(loop);
                    loop->loop();  // 启动事件循环
                });
                t.detach();
            }
        }
    }

    // ==========================================
    // newConnection:新连接到来时的回调
    // 由 acceptor 调用,参数是客户端 socket fd
    // ==========================================
    void newConnection(int conn_fd, const InetAddress& peer_addr) {
        // ========== 步骤 1:选择 subReactor ==========
        // round-robin 选择下一个 EventLoop
        EventLoop* sub_loop = sub_loops_[next_loop_index_++];
        if (next_loop_index_ >= sub_loops_.size()) {
            next_loop_index_ = 0;
        }

        // ========== 步骤 2:在 subReactor 线程中创建 TcpConnection ==========
        // 注意:这里用 runInLoop 投递到 subReactor 线程执行
        // 因为 TcpConnection 创建涉及 Channel 注册,不能跨线程
        sub_loop->runInLoop([this, conn_fd, peer_addr, sub_loop]() {
            // 创建 TcpConnection
            TcpConnectionPtr conn = std::make_shared<TcpConnection>(
                sub_loop,           // 绑定到 subReactor
                conn_fd,            // 客户端 socket fd
                local_addr_,        // 本地地址
                peer_addr           // 客户端地址
            );

            // ========== 步骤 3:设置回调 ==========
            conn->setConnectionCallback(connection_callback_);
            conn->setMessageCallback(message_callback_);
            conn->setWriteCompleteCallback(write_complete_callback_);

            // ========== 步骤 4:开启读事件监听 ==========
            // connectEstablished 会 enableReading,开始处理 IO
            conn->connectEstablished();
        });
    }
};

5.4 subReactor 处理流程

复制代码
subReactor EventLoop 的完整流程:

1. subReactor 创建,调用 loop()
         |
         v
2. epoll_wait() 阻塞等待
         |
         v
3. 有连接发送数据,epoll_wait 返回
         |
         v
4. 遍历 active_channels_
         |
         v
5. Channel.handleEvent() 调用 TcpConnection::OnRead()
         |
         v
6. TcpConnection::OnRead() 读取缓冲区
         |
         v
7. 业务逻辑(CHttpConn 处理)
         |
         v
8. TcpConnection::send() 发送响应
         |
         v
9. 回到步骤 2,继续 loop

6. Channel 与 EventLoop 绑定机制

6.1 关系图

复制代码
EventLoop                           Channel
    |                                 |
    |--- poller_ (EpollPoller)         |
    |--- active_channels_              |
    |                                 |
    |                          +------+------+
    |                          | fd          |
    |                          | events_     |
    |                          | revents_    |
    |                          | callbacks   |
    |                          +------+------+
    |                                 |
    +------- updateChannel() -------->|
    |   // epoll_ctl(ADD/MOD)         |
    |                                 |
    <-------- handleEvent() ----------+
    |   // 根据 revents_ 调用回调       |
    |                                 |
    v                                 |
管理多个 Channel                       |
(通过 poller)                         |

6.2 完整事件流(详细步骤)

复制代码
第一步:应用层创建 TcpConnection
        |
        v
第二步:TcpConnection 构造时创建 Channel,绑定 socket fd
        |
        v
第三步:TcpConnection.enableReading() 启用读事件
        - 设置 channel_->enableReading()
        |
        v
第四步:Channel.enableReading() 修改 events_
        - events_ |= EPOLLIN
        - 调用 loop_->updateChannel(this)
        |
        v
第五步:EventLoop.updateChannel() 更新到 EpollPoller
        - 调用 poller_->updateChannel(ch)
        |
        v
第六步:EpollPoller.updateChannel() 注册到 epoll
        - 调用 epoll_ctl(EPOLL_CTL_ADD, fd, event)
        - channels_[fd] = ch
        |
        v
第七步:Client 发送数据到 socket buffer
        |
        v
第八步:epoll_wait() 返回就绪的 Channel
        - revents_ = EPOLLIN
        |
        v
第九步:EventLoop.loop() 遍历 active_channels_
        - for (ch in active_channels_) ch->handleEvent()
        |
        v
第十步:Channel.handleEvent() 根据 revents_ 调用回调
        - revents_ & EPOLLIN → read_callback_()
        |
        v
第十一步:TcpConnection.OnRead() 读取缓冲区
        - input_buffer_.read(fd)
        |
        v
第十二步:业务逻辑处理(JSON 解析等)
        |
        v
第十三步:TcpConnection.send() 发送响应
        - output_buffer_.append(msg)
        - channel_->enableWriting()
        |
        v
第十四步:EpollPoller 注册 EPOLLOUT 事件
        |
        v
第十五步:socket 可写,epoll_wait 返回
        |
        v
第十六步:Channel.handleEvent() 调用 write_callback_
        - TcpConnection::sendInLoop()
        |
        v
第十七步:数据发送完成,disableWriting
        |
        v
第十八步:回到 epoll_wait

7. TcpConnection 与 CHttpConn

7.1 职责划分

职责 层级
TcpConnection 底层 TCP 连接管理,send/close,读写缓冲区 网络层
CHttpConn HTTP 业务逻辑,请求解析,响应封装 应用层
复制代码
TcpConnection         CHttpConn
+------------+      +------------+
|  socket fd | ---> |  持有指针   |
|  读缓冲区   |      |  解析 HTTP |
|  写缓冲区   |      |  路由分发  |
|  send()    |      |  JSON 处理 |
|  shutdown()|      |  业务逻辑  |
+------------+      +------------+

7.2 TcpConnection 主要接口

复制代码
class TcpConnection : public std::enable_shared_from_this<TcpConnection> {
private:
    EventLoop* loop_;              // 所属 EventLoop
    int fd_;                        // socket fd
    std::string name_;              // 连接名称
    InetAddress local_addr_;       // 本地地址
    InetAddress peer_addr_;         // 客户端地址
    Channel* channel_;             // 关联的 Channel
    Buffer input_buffer_;           // 接收缓冲区
    Buffer output_buffer_;          // 发送缓冲区

public:
    // ==========================================
    // 发送数据
    // 如果在 EventLoop 线程,直接发送
    // 如果在其他线程,投递到 EventLoop
    // ==========================================
    void send(const std::string& msg) {
        if (loop_->isInLoopThread()) {
            sendInLoop(msg);
        } else {
            loop_->runInLoop([this, msg]() { sendInLoop(msg); });
        }
    }

    // ==========================================
    // 关闭连接
    // 通知对方关闭,然后关闭本地 socket
    // ==========================================
    void shutdown() {
        channel_->disableAll();
        loop_->runInLoop([this]() { shutdownInLoop(); });
    }

private:
    void sendInLoop(const std::string& msg) {
        // 写入发送缓冲区
        ssize_t n = output_buffer_.write(fd_, msg.data(), msg.size());

        if (n < 0) {
            // 处理错误
        } else if (n == msg.size()) {
            // 数据全部写入内核缓冲区,禁用写事件
            channel_->disableWriting();
        } else {
            // 部分写入,注册写事件继续发送
            channel_->enableWriting();
        }
    }

    void shutdownInLoop() {
        // 关闭写端(SHUT_WR)
        // 会向对方发送 FIN
        ::shutdown(fd_, SHUT_WR);
    }
};

7.3 CHttpConn 实现

复制代码
class CHttpConn : public std::enable_shared_from_this<CHttpConn> {
private:
    CHttpParserWrapper http_parser_;      // HTTP 解析器
    TcpConnectionPtr tcp_conn_;          // TCP 连接指针
    uint32_t uuid_ = 0;                  // 连接唯一标识

public:
    // ==========================================
    // 构造函数
    // 参数:tcp_conn - TcpConnection 智能指针
    // 作用:从 TcpConnection 的上下文获取 uuid
    // ==========================================
    CHttpConn(TcpConnectionPtr tcp_conn)
        : tcp_conn_(tcp_conn) {
        // 从上下文中提取 uuid
        uuid_ = std::any_cast<uint32_t>(tcp_conn_->getContext());
    }

    // ==========================================
    // OnRead:处理读事件入口
    // 参数:buf - 数据缓冲区
    // ==========================================
    void OnRead(Buffer* buf) {
        const char* data = buf->peek();        // 查看数据,不移动指针
        int len = buf->readableBytes();        // 获取可读字节数

        // ========== 步骤 1:解析 HTTP ==========
        http_parser_.ParseHttpContent(data, len);

        // ========== 步骤 2:检查是否解析完成 ==========
        if (http_parser_.IsReadAll()) {
            // 获取解析结果
            string url = http_parser_.GetUrlString();           // URL 路径
            string body = http_parser_.GetBodyContentString();  // 请求体

            // ========== 步骤 3:路由分发 ==========
            if (strncmp(url.c_str(), "/api/reg", 8) == 0) {
                _HandleRegisterRequest(url, body);
            } else if (strncmp(url.c_str(), "/api/login", 10) == 0) {
                _HandleLoginRequest(url, body);
            } else {
                _HandleNotFound();
            }

            // ========== 步骤 4:消费已解析的数据 ==========
            // 将读缓冲区指针前移,释放内存
            buf->retrieve(len);
        }
    }

private:
    // ==========================================
    // _HandleRegisterRequest:处理注册请求
    // ==========================================
    int _HandleRegisterRequest(string& url, string& post_data) {
        string resp_json;

        // 调用注册业务逻辑
        ApiRegisterUser(post_data, resp_json);

        // 构造 HTTP 响应
        char body[4096];
        snprintf(body, sizeof(body), HTTP_RESPONSE_JSON,
                 resp_json.length(), resp_json.c_str());

        // 发送响应
        tcp_conn_->send(body);

        return 0;
    }

    // ==========================================
    // _HandleLoginRequest:处理登录请求
    // ==========================================
    int _HandleLoginRequest(string& url, string& post_data) {
        string resp_json;

        // 调用登录业务逻辑
        ApiLoginUser(post_data, resp_json);

        // 构造 HTTP 响应
        char body[4096];
        snprintf(body, sizeof(body), HTTP_RESPONSE_JSON,
                 resp_json.length(), resp_json.c_str());

        // 发送响应
        tcp_conn_->send(body);

        return 0;
    }

    // ==========================================
    // _HandleNotFound:处理 404
    // ==========================================
    void _HandleNotFound() {
        string resp = "{\"code\": 404}";
        char body[256];
        snprintf(body, sizeof(body), HTTP_RESPONSE_JSON,
                 resp.length(), resp.c_str());
        tcp_conn_->send(body);
    }
};

7.4 响应宏定义

复制代码
// ==========================================
// HTTP 响应格式宏
// 组成:状态行 + 响应头 + 空行 + 响应体
// ==========================================

// JSON 响应格式
// Content-Type: application/json
#define HTTP_RESPONSE_JSON \
    "HTTP/1.1 200 OK\r\n" \
    "Connection:close\r\n" \
    "Content-Length:%d\r\n" \
    "Content-Type:application/json;charset=utf-8\r\n\r\n%s"

// HTML 响应格式
// Content-Type: text/html
#define HTTP_RESPONSE_HTML \
    "HTTP/1.1 200 OK\r\n" \
    "Connection:close\r\n" \
    "Content-Length:%d\r\n" \
    "Content-Type:text/html;charset=utf-8\r\n\r\n%s"

// 错误响应格式
// 400 Bad Request
#define HTTP_RESPONSE_BAD_REQ \
    "HTTP/1.1 400 Bad Request\r\n" \
    "Connection:close\r\n" \
    "Content-Length:%d\r\n" \
    "Content-Type:application/json;charset=utf-8\r\n\r\n%s"

HTTP 响应格式分解:

复制代码
HTTP/1.1 200 OK                    ← 状态行
Connection:close                   ← 响应头(关闭连接)
Content-Length:12                  ← 响应头(body 长度)
Content-Type:application/json...   ← 响应头(内容类型)
                                   ← 空行(\r\n\r\n)
{"code": 0}                        ← 响应体(JSON)

8. HTTP 请求解析与 JSON 处理

8.1 HTTP Parser 封装

复制代码
class CHttpParserWrapper {
private:
    http_parser parser_;               // 原始 http_parser
    http_parser_settings settings_;    // 回调配置
    bool read_all_ = false;            // 是否解析完成
    uint32_t total_length_ = 0;        // 总长度
    string url_;                       // URL
    string body_content_;              // 请求体
    string host_;                      // Host 头
    string user_agent_;                // User-Agent 头

public:
    CHttpParserWrapper() {
        // ========== 步骤 1:初始化 settings ==========
        // 设置各种回调函数
        settings_.on_url = OnUrl;                    // URL 解析完成
        settings_.on_body = OnBody;                  // 请求体解析
        settings_.on_header_field = OnHeaderField;  // Header 字段
        settings_.on_header_value = OnHeaderValue;  // Header 值
        settings_.on_headers_complete = OnHeadersComplete;  // Headers 结束
        settings_.on_message_complete = OnMessageComplete;  // 消息结束
    }

    // ==========================================
    // ParseHttpContent:解析 HTTP 内容
    // 参数:
    //   buf - HTTP 原始数据
    //   len - 数据长度
    // ==========================================
    void ParseHttpContent(const char* buf, uint32_t len) {
        // 第一步:初始化 parser
        http_parser_init(&parser_, HTTP_REQUEST);
        parser_.data = this;  // 传递 this 指针,供回调使用

        // 第二步:执行解析
        // 会触发各种回调函数
        size_t nparsed = http_parser_execute(
            &parser_,
            &settings_,
            buf,
            len
        );

        // 第三步:检查解析结果
        if (nparsed == len && parser_.http_errno == HPE_OK) {
            read_all_ = true;
        }
    }

    // getters
    bool IsReadAll() { return read_all_; }
    string& GetUrlString() { return url_; }
    string& GetBodyContentString() { return body_content_; }
};

// ==========================================
// 静态回调:URL 解析完成
// 参数:
//   parser - http_parser 对象
//   at - URL 字符串起始位置
//   length - URL 长度
//   obj - 传入的 CHttpParserWrapper 指针
// ==========================================
int CHttpParserWrapper::OnUrl(http_parser* parser,
                               const char* at, size_t length, void* obj) {
    auto* wrapper = static_cast<CHttpParserWrapper*>(obj);
    wrapper->SetUrl(at, length);  // 追加到 url_ 字符串
    return 0;  // 返回 0 表示成功
}

// ==========================================
// 静态回调:Body 解析完成
// ==========================================
int CHttpParserWrapper::OnBody(http_parser* parser,
                               const char* at, size_t length, void* obj) {
    auto* wrapper = static_cast<CHttpParserWrapper*>(obj);
    wrapper->SetBodyContent(at, length);  // 追加到 body_content_
    return 0;
}

// ==========================================
// 静态回调:Headers 解析完成
// ==========================================
int CHttpParserWrapper::OnHeadersComplete(http_parser* parser,
                                          void* obj) {
    auto* wrapper = static_cast<CHttpParserWrapper*>(obj);
    // 可以在这里获取 Content-Length 等信息
    return 0;
}

// ==========================================
// 静态回调:整个消息解析完成
// ==========================================
int CHttpParserWrapper::OnMessageComplete(http_parser* parser,
                                          void* obj) {
    auto* wrapper = static_cast<CHttpParserWrapper*>(obj);
    wrapper->SetReadAll();  // 设置解析完成标志
    return 0;
}

8.2 JSON 序列化与反序列化

复制代码
// JSON 序列化:将对象转换为 JSON 字符串
// JSON 反序列化:将 JSON 字符串解析为对象

// ==========================================
// 反序列化:解析注册信息
// 输入:{"userName":"zhangsan","nickName":"张三",...}
// 输出:user_name, nick_name, pwd, phone, email
// ==========================================
int decodeRegisterJson(const string& str_json,
                       string& user_name, string& nick_name,
                       string& pwd, string& phone, string& email) {
    // 第一步:创建 JSON 读取器
    Json::Value root;
    Json::Reader reader;

    // 第二步:解析 JSON 字符串
    if (!reader.parse(str_json, root)) {
        // 解析失败,JSON 格式错误
        return -1;
    }

    // 第三步:提取必填字段
    // 如果字段不存在或为 null,isNull() 返回 true
    if (root["userName"].isNull()) return -1;
    user_name = root["userName"].asString();  // 转为 string

    if (root["nickName"].isNull()) return -1;
    nick_name = root["nickName"].asString();

    if (root["firstPwd"].isNull()) return -1;
    pwd = root["firstPwd"].asString();

    // 第四步:提取可选字段
    if (!root["phone"].isNull()) {
        phone = root["phone"].asString();
    }
    if (!root["email"].isNull()) {
        email = root["email"].asString();
    }

    return 0;
}

// ==========================================
// 序列化:封装响应
// 输入:code = 0 或 1
// 输出:{"code":0} 或 {"code":1}
// ==========================================
int encodeRegisterJson(int code, string& str_json) {
    // 第一步:创建根对象
    Json::Value root;
    root["code"] = code;  // 0 = 成功,非 0 = 失败

    // 第二步:创建 FastWriter(效率高)
    Json::FastWriter writer;

    // 第三步:序列化为字符串
    str_json = writer.write(root);

    return 0;
}

9. 登录注册 API 实现

9.1 注册 API 流程

复制代码
注册请求流程:

POST /api/reg
Content-Type: application/json

{
    "userName": "zhangsan",
    "nickName": "张三",
    "firstPwd": "123456",
    "phone": "13800138000",
    "email": "zhangsan@example.com"
}
                |
                v
+-----------------------------------------------+
| 步骤 1:HTTP Parser 解析                      |
| - 解析请求行:POST /api/reg HTTP/1.1          |
| - 解析 Header:Content-Type 等                |
| - 解析 Body:JSON 字符串                      |
+-----------------------------------------------+
                |
                v
+-----------------------------------------------+
| 步骤 2:decodeRegisterJson()                  |
| - 解析 JSON                                   |
| - 提取 userName, nickName, firstPwd          |
| - 提取 phone, email(可选)                   |
+-----------------------------------------------+
                |
                v
+-----------------------------------------------+
| 步骤 3:registerUser()                       |
| - 检查用户名是否已存在(数据库查询)            |
| - 检查昵称是否已存在                          |
| - 密码加密                                    |
| - 插入 user 表                                |
+-----------------------------------------------+
                |
                v
+-----------------------------------------------+
| 步骤 4:encodeRegisterJson()                  |
| - 构造响应 JSON:{"code": 0}                  |
+-----------------------------------------------+
                |
                v
        HTTP Response 200 OK
        {"code": 0}

9.2 注册 API 伪代码

复制代码
// api_register.h
int ApiRegisterUser(string& post_data, string& resp_json);


// api_register.cc

// ==========================================
// decodeRegisterJson:反序列化注册信息
// ==========================================
int decodeRegisterJson(const string& str_json,
                       string& user_name, string& nick_name,
                       string& pwd, string& phone, string& email) {
    // 第一步:创建 JSON 读取器
    Json::Value root;
    Json::Reader reader;

    // 第二步:解析 JSON
    if (!reader.parse(str_json, root)) {
        return -1;  // JSON 格式错误
    }

    // 第三步:提取 userName(必填)
    if (root["userName"].isNull()) {
        return -1;  // 缺少必填字段
    }
    user_name = root["userName"].asString();

    // 第四步:提取 nickName(必填)
    if (root["nickName"].isNull()) {
        return -1;
    }
    nick_name = root["nickName"].asString();

    // 第五步:提取 firstPwd(必填)
    if (root["firstPwd"].isNull()) {
        return -1;
    }
    pwd = root["firstPwd"].asString();

    // 第六步:提取 phone(可选)
    if (root["phone"].isNull()) {
        phone = "";  // 空字符串表示未提供
    } else {
        phone = root["phone"].asString();
    }

    // 第七步:提取 email(可选)
    if (root["email"].isNull()) {
        email = "";
    } else {
        email = root["email"].asString();
    }

    return 0;
}

// ==========================================
// encodeRegisterJson:序列化响应
// ==========================================
int encodeRegisterJson(int code, string& str_json) {
    Json::Value root;
    root["code"] = code;

    Json::FastWriter writer;
    str_json = writer.write(root);

    return 0;
}

// ==========================================
// registerUser:写入数据库(伪实现)
// ==========================================
int registerUser(string& user_name, string& nick_name,
                 string& pwd, string& phone, string& email) {
    // TODO: 连接数据库,执行以下操作

    // 步骤 1:检查用户名是否已存在
    // SELECT * FROM user WHERE user_name = ?

    // 步骤 2:检查昵称是否已存在
    // SELECT * FROM user WHERE nick_name = ?

    // 步骤 3:对密码进行加密
    // pwd = hash(pwd)  // 使用 SHA256 等加密算法

    // 步骤 4:插入用户记录
    // INSERT INTO user (user_name, nick_name, pwd, phone, email)
    // VALUES (?, ?, ?, ?, ?)

    return 0;
}

// ==========================================
// ApiRegisterUser:注册 API 入口
// ==========================================
int ApiRegisterUser(string& post_data, string& resp_json) {
    string user_name, nick_name, pwd, phone, email;

    // ========== 步骤 1:检查数据是否为空 ==========
    if (post_data.empty()) {
        encodeRegisterJson(1, resp_json);  // code = 1 表示失败
        return -1;
    }

    // ========== 步骤 2:解析 JSON ==========
    if (decodeRegisterJson(post_data, user_name, nick_name, pwd,
                           phone, email) < 0) {
        encodeRegisterJson(1, resp_json);
        return -1;
    }

    // ========== 步骤 3:写入数据库 ==========
    int ret = registerUser(user_name, nick_name, pwd, phone, email);

    // ========== 步骤 4:返回响应 ==========
    encodeRegisterJson(ret, resp_json);
    return 0;
}

9.3 登录 API 流程

复制代码
登录请求流程:

POST /api/login
Content-Type: application/json

{
    "user": "zhangsan",
    "pwd": "123456"
}
                |
                v
+-----------------------------------------------+
| 步骤 1:HTTP Parser 解析                       |
| - 解析请求行:POST /api/login HTTP/1.1        |
| - 解析 Body:JSON 字符串                      |
+-----------------------------------------------+
                |
                v
+-----------------------------------------------+
| 步骤 2:decodeLoginJson()                     |
| - 解析 JSON                                   |
| - 提取 user, pwd                              |
+-----------------------------------------------+
                |
                v
+-----------------------------------------------+
| 步骤 3:verifyUserPassword()                  |
| - 根据 user 查询数据库                        |
| - 验证密码是否匹配                            |
+-----------------------------------------------+
                |
                v
+-----------------------------------------------+
| 步骤 4:setToken()                            |
| - 生成唯一 Token(UUID 或 JWT)               |
| - 存储到 Redis                                |
+-----------------------------------------------+
                |
                v
+-----------------------------------------------+
| 步骤 5:encodeLoginJson()                     |
| - 构造响应 JSON:{"code": 0, "token": "xxx"}  |
+-----------------------------------------------+
                |
                v
        HTTP Response 200 OK
        {"code": 0, "token": "abc123..."}

9.4 登录 API 伪代码

复制代码
// api_login.h
int ApiLoginUser(string& post_data, string& resp_json);


// api_login.cc

// ==========================================
// decodeLoginJson:反序列化登录信息
// ==========================================
int decodeLoginJson(const string& str_json,
                    string& user_name, string& pwd) {
    Json::Value root;
    Json::Reader reader;

    if (!reader.parse(str_json, root)) {
        return -1;
    }

    if (root["user"].isNull()) {
        return -1;
    }
    user_name = root["user"].asString();

    if (root["pwd"].isNull()) {
        return -1;
    }
    pwd = root["pwd"].asString();

    return 0;
}

// ==========================================
// encodeLoginJson:序列化登录响应
// ==========================================
int encodeLoginJson(int code, string& token, string& str_json) {
    Json::Value root;
    root["code"] = code;

    // 只有成功时才包含 token
    if (code == 0) {
        root["token"] = token;
    }

    Json::FastWriter writer;
    str_json = writer.write(root);

    return 0;
}

// ==========================================
// verifyUserPassword:验证用户名密码(伪实现)
// ==========================================
int verifyUserPassword(string& user_name, string& pwd) {
    // TODO: 连接数据库

    // 步骤 1:根据 user_name 查询用户
    // SELECT pwd FROM user WHERE user_name = ?

    // 步骤 2:验证密码是否匹配
    // if (stored_pwd == hash(pwd)) return 0;

    return 0;
}

// ==========================================
// setToken:生成并存储 Token(伪实现)
// ==========================================
int setToken(string& user_name, string& token) {
    // 步骤 1:生成唯一 token
    // 使用 UUID 或 JWT
    token = generateUUID();

    // 步骤 2:存储到 Redis
    // redis.set("token:" + token, user_name)
    // redis.expire("token:" + token, 86400)  // 24 小时过期

    return 0;
}

// ==========================================
// ApiLoginUser:登录 API 入口
// ==========================================
int ApiLoginUser(string& post_data, string& resp_json) {
    string user_name, pwd, token;

    // ========== 步骤 1:检查数据是否为空 ==========
    if (post_data.empty()) {
        encodeLoginJson(1, token, resp_json);
        return -1;
    }

    // ========== 步骤 2:解析 JSON ==========
    if (decodeLoginJson(post_data, user_name, pwd) < 0) {
        encodeLoginJson(1, token, resp_json);
        return -1;
    }

    // ========== 步骤 3:验证用户名密码 ==========
    if (verifyUserPassword(user_name, pwd) < 0) {
        encodeLoginJson(1, token, resp_json);
        return -1;
    }

    // ========== 步骤 4:生成 Token ==========
    if (setToken(user_name, token) < 0) {
        encodeLoginJson(1, token, resp_json);
        return -1;
    }

    // ========== 步骤 5:返回成功响应 ==========
    encodeLoginJson(0, token, resp_json);
    return 0;
}

9.5 URL 路由分发

复制代码
// CHttpConn::OnRead 中的路由逻辑

void CHttpConn::OnRead(Buffer* buf) {
    // ... HTTP 解析 ...

    string url = http_parser_.GetUrlString();
    string body = http_parser_.GetBodyContentString();

    // ==========================================
    // URL 路由表
    // 使用 strncmp 进行前缀匹配
    // ==========================================

    // 注册
    if (strncmp(url.c_str(), "/api/reg", 8) == 0) {
        _HandleRegisterRequest(url, body);
    }
    // 登录
    else if (strncmp(url.c_str(), "/api/login", 10) == 0) {
        _HandleLoginRequest(url, body);
    }
    // 图片上传(待实现)
    else if (strncmp(url.c_str(), "/api/upload", 11) == 0) {
        _HandleUploadRequest(url, body);
    }
    // 图片列表(待实现)
    else if (strncmp(url.c_str(), "/api/list", 9) == 0) {
        _HandleListRequest(url, body);
    }
    // 图片访问(待实现):/i/abc123.png
    else if (strncmp(url.c_str(), "/i/", 3) == 0) {
        _HandleImageRequest(url);
    }
    // 未知路径
    else {
        _HandleNotFound();
    }
}

10. 总结

10.1 核心要点回顾

概念 说明
EventLoop 每个线程一个,负责 epoll_wait 循环,runInLoop 支持跨线程任务投递
EpollPoller 对 epoll 的封装,updateChannel 管理 ADD/MOD/DEL
Channel 封装 fd 和事件回调,enableReading/enableWriting 控制事件,handleEvent 分发回调
TcpServer 服务端,管理 acceptor,setThreadNum 设置 subReactor 数量,round-robin 分发连接
TcpConnection 底层 TCP 连接管理,send/close,读写缓冲区,enableReading 开启读事件
CHttpConn HTTP 业务层,解析请求,路由分发到 API 函数
std::any 类型擦除容器,用于 TcpConnection 上下文传递 uuid
http-parser node.js 的 HTTP 解析器,静态回调模式
jsoncpp JSON 序列化/反序列化,FastWriter 高效序列化

10.2 请求处理流程总览

复制代码
Client TCP Connect
       |
       v
mainReactor accept()
       |
       v (round-robin)
subReactor[i] 创建 TcpConnection + Channel
       |
       v
Channel.enableReading() 注册 EPOLLIN 到 epoll
       |
       v
epoll_wait() 返回就绪事件
       |
       v
Channel.handleEvent() 调用 TcpConnection.OnRead()
       |
       v
CHttpConn.OnRead() -> HTTP Parse
       |
       v
decodeRegisterJson / decodeLoginJson -> JSON 解析
       |
       v
registerUser / verifyUserPassword -> 业务处理
       |
       v
encodeRegisterJson / encodeLoginJson -> JSON 封装
       |
       v
TcpConnection.send() 发送 HTTP Response
       |
       v
Client TCP Close

10.3 设计思想

  1. 事件驱动:所有操作都是对事件的响应,epoll_wait 阻塞直到有事件发生
  2. 线程私有:EventLoop 绑定线程,不跨线程使用,runInLoop 保证线程安全
  3. 对象池管理:通过 shared_ptr 管理 TcpConnection/CHttpConn 生命周期
  4. 上下文传递:通过 std::any 在连接生命周期内传递自定义数据(uuid)
  5. 分层设计:网络层(TcpConnection)和业务层(CHttpConn)分离
  6. Reactor 模式:One Loop Per Thread,IO 和计算分离

根据零声教育教学写作https://github .com/0voice

相关推荐
liulilittle2 小时前
论 Linux 内核态全局稳态带宽的卡尔曼估计与工程实现
linux·服务器·网络·c++·计算机网络·tcp·通信
pusheng20252 小时前
IFSJ全英文专访:中国创新力量重塑先进气体感知技术,赋能全球关键基础设施安全
前端·网络·人工智能·物联网·安全
Irissgwe2 小时前
五、应用层协议HTTP
linux·网络·网络协议·http·状态码·url
自动跟随5 小时前
UWB自动跟随技术全栈解析:从定位算法到“位控一体化“
java·网络·人工智能
长和信泰光伏储能5 小时前
远离电网的底气:离网光伏系统核心原理与搭建要点
网络
天天进步20155 小时前
Tunnelto 源码解析 #8:多路复用机制:StreamId、ActiveStreams 与并发请求生命周期
网络
数智化管理手记6 小时前
标准作业越推越虚?重塑认知、规避误区,破解精益落地形式主义
大数据·网络·精益工程
国科安芯7 小时前
ASP7A84AS——航天级低噪声高PSRR线性稳压器
网络·单片机·嵌入式硬件·架构·安全性测试
以太浮标8 小时前
华为eNSP模拟器综合实验之- 路由黑洞场景解析及实验
运维·网络·网络协议·网络安全·华为·智能路由器·信息与通信