My_Server思维导图
5124 字
26 分钟
My_Server思维导图
- 1HTTP
- 2My_Server思维导图本文
- 3流程梳理
- 4My_server框架全流程梳理
🌐 My_Server:C++ Reactor 网络服务器
-
🎯 项目定位
- 项目类型:基于 Linux 系统调用实现的 C++11 网络服务器框架
- 核心模式:Reactor 事件驱动 + 非阻塞 I/O + 主从事件循环 + 回调式业务层
- 能力边界
- 底层 TCP 网络通信
- epoll I/O 多路复用
- 跨线程任务调度
- 时间轮定时器与非活跃连接回收
- HTTP/1.0、HTTP/1.1 请求解析与响应
- 正则路由与静态文件服务
- 运行平台依赖
- Linux Socket API
epolleventfdtimerfd- POSIX signal
- pthread
- 总体分层
- HTTP 应用示例层
http/main.cc + http/wwwroot
- HTTP 协议层
http/http.hppHttpServer / HttpContext / HttpRequest / HttpResponse / Util
- TCP 与连接管理层
server.hppTcpServer / Acceptor / Connection / Socket / Buffer
- Reactor 与并发层
EventLoop / Poller / ChannelLoopThread / LoopThreadPool
- 定时与通用设施层
TimerWheel / TimerTask / Any / NetWork / LOG
- HTTP 应用示例层
-
🧭 一眼看懂核心对象关系
HttpServer- 组合一个
TcpServer - 向
TcpServer注册OnConnected / OnMessage - 维护四张 HTTP 路由表
- 调用
Util完成文件、URL、MIME、状态码处理
- 组合一个
TcpServer- 拥有一个主线程
_baseloop : EventLoop - 拥有一个
_acceptor : Acceptor - 拥有一个
_pool : LoopThreadPool - 拥有连接表
_conns : unordered_map<uint64_t, PtrConnection>
- 拥有一个主线程
Acceptor- 组合监听
_socket : Socket - 组合监听
_channel : Channel Channel可读 →Acceptor::HandleRead→Socket::Accept- Accept 成功 → 回调
TcpServer::NewConnection
- 组合监听
Connection- 组合通信
Socket - 组合事件
Channel - 组合输入与输出两个
Buffer - 持有所属
EventLoop* - 用
Any保存协议上下文 - 由
shared_ptr管理生命周期
- 组合通信
EventLoop- 组合
_poller : Poller - 组合
_timer_wheel : TimerWheel - 组合 eventfd 对应的
_event_channel : Channel - 串行处理 I/O 事件与跨线程任务
- 组合
Poller- 维护
fd → Channel* - 调用
epoll_ctl / epoll_wait
- 维护
Channel- 绑定一个 fd 与一个
EventLoop* - 保存关注事件、就绪事件及五类回调
- 自己不直接操作 epoll
Update / Remove委托EventLoop,再由Poller操作 epoll
- 绑定一个 fd 与一个
TimerWheel- 绑定一个
EventLoop* - 使用
timerfd + Channel获得每秒 tick - 使用
shared_ptr<TimerTask>控制定时任务寿命
- 绑定一个
- 主调用链口诀
- 应用 →
HttpServer→TcpServer TcpServer→Acceptor + LoopThreadPool + ConnectionConnection→Channel + Socket + Buffer + AnyChannel→EventLoop→Poller→ epoll
- 应用 →
-
🌊 一次 TCP 连接的完整生命周期
- ① 服务器启动
- 应用构造
TcpServer(port) TcpServer构造主_baseloopTcpServer构造_acceptor(&_baseloop, port)Acceptor创建、复用地址、绑定、监听、设为非阻塞Acceptor::Linst使能监听 fd 的读事件TcpServer::Start_pool.Create()创建 Sub Reactor 线程_baseloop.Start()启动 Main Reactor
- 应用构造
- ② 新连接到来
- epoll 报告监听 fd 可读
Poller::Poll找到监听ChannelChannel::HandleEvent调用读回调Acceptor::HandleRead调用Socket::AcceptAcceptor::_accept_callback(newfd)调用TcpServer::NewConnection
- ③ 创建并分配连接
TcpServer::_next_id++LoopThreadPool::NextLoop轮询选择一个 Sub Reactor- 创建
shared_ptr<Connection>(loop, conn_id, fd) - 把业务层四类回调下发给
Connection - 设置服务器内部关闭回调
TcpServer::RemoveConnection - 可选启用非活跃超时释放
Connection::Established投递到所属 EventLoop- 存入
_conns[conn_id]保持对象生命周期
- ④ 连接建立完成
EstablishedInLoopCONNECTING → CONNECTED_channel.EnableRead()- 调用业务
_connected_callback
- HTTP 场景
HttpServer::OnConnectedconn->SetContext(HttpContext())
- ⑤ 收取数据
Connection::HandleReadSocket::NonBlockRecv- 数据写入
_in_buffer - 调用
_message_callback(conn, &_in_buffer)
- HTTP 回调进入
HttpServer::OnMessage
- ⑥ 发送数据
- 业务调用
Connection::Send(data, len) - 临时
Buffer拷贝外部数据,保证跨线程生命周期 EventLoop::RunInLoop调用SendInLoop- 数据追加到
_out_buffer _channel.EnableWrite()开启 EPOLLOUT- epoll 报告可写后调用
HandleWrite Socket::NonBlockSend发送可读区数据- 移动
_out_buffer读偏移 - 全部发完后
DisableWrite,避免 LT 模式空转
- 业务调用
- ⑦ 活跃度刷新
Channel::HandleEvent最后调用任意事件回调Connection::HandleEvent- 若启用超时释放,调用
EventLoop::TimerRefresh(conn_id) - 再调用应用层
_event_callback
- 若启用超时释放,调用
- ⑧ 关闭与释放
- 业务调用
Connection::ShutDown ShutdownInLoop- 状态改为
DISCONNECTING - 先处理剩余输入
- 若仍有输出则继续等待可写
- 输出清空后调用
Release
- 状态改为
Release强制用QueueInLoop延迟执行ReleaseInLoopReleaseInLoop- 状态改为
DISCONNECTED - 从 Poller 移除 Channel
- 关闭 Socket
- 取消非活跃定时器
- 调用应用关闭回调
- 调用服务器关闭回调
- 状态改为
TcpServer::RemoveConnection- 将删除动作投回主
_baseloop RemoveConnectionInLoop从_conns删除- 引用计数归零后析构
Connection
- 将删除动作投回主
- 业务调用
- ① 服务器启动
-
📨 一次 HTTP 请求的完整数据流
- ① TCP 数据进入
epoll_wait- ↓
Poller::Poll Channel::HandleEvent- ↓ 读回调
Connection::HandleRead- ↓
Socket::NonBlockRecv Connection::_in_buffer
- ② 进入 HTTP 层
Connection::_message_callback- ↓
HttpServer::OnMessage(conn, buffer)- 从
conn->_context : Any取出HttpContext*
- ③ 增量解析
HttpContext::RecvHttpRequest(buffer)- 状态按顺序推进
RECV_HTTP_LINERECV_HTTP_HEADRECV_HTTP_BODYRECV_HTTP_OVER
- 解析结果持续写入同一个
HttpRequest - 数据不完整则保留状态,等待下一次可读事件
- ④ 路由
- 完整请求 →
HttpServer::Route - 优先尝试静态文件
IsFileHandlerFileHandler
- 否则按方法选择路由表
- GET/HEAD →
_get_route - POST →
_post_route - PUT →
_put_route - DELETE →
_delete_route
- GET/HEAD →
Dispatcher用正则匹配_path- 命中 → 调用业务
Handler(req, rsp) - 未命中 → 404
- 完整请求 →
- ⑤ 构造响应
- 业务处理器填写
HttpResponse WriteResponse- 根据请求决定
Connection: close/keep-alive - 补充
Content-length - 补充默认
Content-Type - 重定向时补充
Location - 拼接状态行、响应头、空行、正文
- 根据请求决定
conn->Send回到底层异步发送链
- 业务处理器填写
- ⑥ 请求结束
HttpContext::ReSet为下一条请求复用连接- 短连接 →
conn->ShutDown - 长连接 → 保持连接,继续解析下一条请求
- ⑦ 解析错误
HttpContext::_resp_statu >= 400ErrorHandler生成简单 HTML 错误页WriteResponse- 清空剩余输入
conn->ShutDown
- ① TCP 数据进入
-
🧱 底层缓冲区
Buffer·server.hpp- 定位:TCP 输入、输出及 HTTP 增量解析共享的动态字节缓冲区
- 私有数据
_buffer : vector<char>:底层连续空间,默认 1024 字节_reader_idx : uint64_t:可读区起点_writer_idx : uint64_t:可写区起点
- 空间布局
[0, reader):头部空闲区[reader, writer):可读数据区[writer, size):尾部空闲区
- 地址与容量接口
Begin():底层首地址WritePosition():当前写指针ReadPosition():当前读指针HandIdleSize():头部空闲大小TailIdleSize():尾部空闲大小ReadAbleSize():可读字节数
- 偏移接口
MoveReadOffset(len):消费数据MoveWriteOffset(len):确认写入数据
- 空间整理
EnsureWriteSpace(len)- 尾部足够 → 不处理
- 尾部不足但总空闲足够 → 可读数据前移
- 总空闲不足 →
vector::resize
- 写接口
Write(data, len):复制但不移动写偏移WriteAndPush(data, len):复制并移动写偏移WriteString / WriteStringAndPushWriteBuffer / WriteBufferAndPush
- 读接口
Read(buf, len):复制但不移动读偏移ReadAndPop(buf, len):复制并消费ReadAsString / ReadAsStringAndPopFindCRLF():查找换行符GetLine / GetLineAndPop:供 HTTP 行解析使用Clear():读写偏移归零
- 主要使用者
Connection::_in_bufferConnection::_out_bufferHttpContext按行与按 Content-Length 消费输入
-
🔌 套接字封装
Socket·server.hpp- 私有数据
_sockfd : int
- 生命周期
- 默认 fd 为
-1 - 可接管已有 fd
- 析构调用
Close
- 默认 fd 为
- 基础接口
FdCreateBindListenConnectAcceptRecv / SendNonBlockRecv / NonBlockSendClose
- 组合流程
CreateServer(port, ip, block_flag)- Create → ReuseAddress → 可选 NonBlock → Bind → Listen
CreateClient(port, ip)- Create → Connect
- 配置接口
ReuseAddressSO_REUSEADDRSO_REUSEPORT
NonBlockfcntl + O_NONBLOCK
- 关系
Acceptor::_socket:监听套接字Connection::_socket:客户端通信套接字
- 私有数据
-
⚡ 事件通道
Channel·server.hpp- 定位:一个 fd 在 Reactor 中的事件与回调载体
- 私有数据
_fd : int_loop : EventLoop*_events : uint32_t:希望监控的事件_revents : uint32_t:本轮实际就绪事件
- 回调类型
EventCallBack = function<void()> - 五类回调
_read_callback_write_callback_error_callback_close_callback_event_callback
- 查询与设置
Fd / Events / SetREventsSetReadCallBack / SetWriteCallBackSetErrorCallBack / SetCloseCallBack / SetEventCallBackReadAble / WriteAble
- 事件开关
EnableRead / DisableReadEnableWrite / DisableWriteDisableAll- 每次变化调用
Update
- 委托接口
Update()→_loop->UpdateEvent(this)Remove()→_loop->RemoveEvent(this)
- 分发接口
HandleEventEPOLLIN / EPOLLRDHUP / EPOLLPRI→ 读回调EPOLLOUT→ 写回调- 否则
EPOLLERR→ 错误回调 - 否则
EPOLLHUP→ 关闭回调 - 最后统一调用任意事件回调
- 被谁拥有
- 每个
Connection一个通信 Channel Acceptor一个监听 ChannelEventLoop一个 eventfd ChannelTimerWheel一个 timerfd Channel
- 每个
-
🔭 I/O 多路复用
Poller·server.hpp- 定位:对 Linux epoll 的直接封装
- 私有数据
_epfd : int_evs[MAX_EPOLLEVENTS] : epoll_event[1024]_channels : unordered_map<int, Channel*>
- 内部接口
Update(channel, op)→epoll_ctlHasChannel(channel):检查 fd 是否登记
- 对外接口
UpdateEvent- 未登记 → map 插入 +
EPOLL_CTL_ADD - 已登记 →
EPOLL_CTL_MOD
- 未登记 → map 插入 +
RemoveEvent- map 删除 +
EPOLL_CTL_DEL
- map 删除 +
Poll(active*)epoll_wait(..., -1)- fd 反查
Channel* SetREvents- 加入活跃 Channel 列表
- 上下游关系
- 上游:只由
EventLoop调用 - 下游:直接操作 epoll 内核接口
- 输出:活跃
Channel*列表交回EventLoop
- 上游:只由
-
🔁 事件循环
EventLoop·server.hpp- 定位:每个 Reactor 线程的串行执行核心
- 私有数据
_thread_id : thread::id_event_fd : int_event_channel : unique_ptr<Channel>_poller : Poller_tasks : vector<Functor>_mutex : mutex_timer_wheel : TimerWheel
- 类型
Functor = function<void()>
- 主循环
StartPoller::Poll阻塞取就绪事件- 遍历
Channel::HandleEvent RunAllTask执行跨线程任务
- 跨线程唤醒
CreateEventFd创建非阻塞 eventfd_event_channel监听 eventfd 可读QueueInLoop- 加锁追加
_tasks WakeUpEventFd
- 加锁追加
- eventfd 可读 →
ReadEventFd清除唤醒计数 RunAllTask- 锁内 swap 到局部数组
- 锁外逐个执行
- 线程归属接口
IsInLoopAssertInLoopRunInLoop- 当前线程 → 立即执行
- 其他线程 →
QueueInLoop
- 事件代理
UpdateEvent→Poller::UpdateEventRemoveEvent→Poller::RemoveEvent
- 定时器代理
TimerAddTimerRefreshTimerCancelHasTimer
- 在服务器中的两种角色
- Main Reactor
TcpServer::_baseloop- 监听新连接
- 串行修改
_conns
- Sub Reactor
- 每个
LoopThread内一个 - 处理已连接客户端读写
- 每个
- Main Reactor
-
⏰ 时间轮
TimerTask + TimerWheel·server.hpp- 类型别名
TaskFunc = function<void()>ReleaseFunc = function<void()>
TimerTask- 数据
_id : uint64_t_timeout : uint32_t_canceled : bool_task_cb : TaskFunc_release : ReleaseFunc
- 生命周期语义
- 最后一个
shared_ptr释放时析构 - 未取消 → 执行
_task_cb - 随后执行
_release清理映射
- 最后一个
- 接口
CancelSetReleaseDelayTime
- 数据
TimerWheel- 类型
PtrTask = shared_ptr<TimerTask>WeakTask = weak_ptr<TimerTask>
- 数据
_tick : int:当前秒针_capacity : int:60 个刻度_wheel : vector<vector<PtrTask>>_timers : unordered_map<uint64_t, WeakTask>_loop : EventLoop*_timerfd : int_timer_channel : unique_ptr<Channel>
- tick 驱动
CreateTimerFd设置首到期 1 秒、周期 1 秒- timerfd 可读 →
OnTime ReadTimeFd获取累计超时次数- 每次
RunTimerTask- 秒针前进
- 清空当前槽
- 释放任务的最后引用时触发
TimerTask析构回调
- 增删改查
TimerAddInLoop- 新建
TimerTask - 计算
(tick + delay) % capacity - 放入时间轮
- weak_ptr 登记到
_timers
- 新建
TimerRefreshInLoop- weak_ptr 提升为 shared_ptr
- 将同一任务再次放入新到期槽
TimerCancelInLoop- 设置 canceled,析构时不执行业务任务
HasTimerRemoveTimer
- 线程安全外壳
TimerAdd / TimerRefresh / TimerCancel- 均通过
_loop->RunInLoop串行执行 InLoop 版本
- 类型
- 与连接的关系
conn_id同时用作超时定时器 id- 新连接可调用
EnableInactiveRelease - 任意 I/O 事件调用
TimerRefresh - 超时任务调用
Connection::Release - 正常关闭调用
TimerCancel
- 类型别名
-
🧵 线程封装
LoopThread + LoopThreadPool·server.hppLoopThread- 数据
_mutex_cond : condition_variable_loop : EventLoop*_thread : thread
- 构造即启动线程
ThreadEntry- 在线程栈中创建
EventLoop loop - 保存
_loop = &loop notify_allloop.Start
- 在线程栈中创建
GetLoop- 等待
_loop != nullptr - 保证调用者拿到已初始化的 EventLoop
- 等待
- 数据
LoopThreadPool- 数据
_thread_count : int_next_idx : int_baseloop : EventLoop*_threads : vector<LoopThread*>_loops : vector<EventLoop*>
SetThreadCountCreate- 按配置创建
LoopThread - 调用
GetLoop收集 Sub Reactor
- 按配置创建
NextLoop- 线程数为 0 → 返回主 EventLoop
- 否则 Round-Robin 返回下一个 Sub Reactor
- 数据
- 与
TcpServer的关系TcpServer拥有线程池TcpServer::Start时创建线程NewConnection时分配连接所属 EventLoop
-
📦 类型擦除容器
Any·server.hpp- 定位:让
Connection保存任意协议的连接级上下文 - 内部抽象基类
holder- 虚析构
type()clone()
- 模板子类
placeholder<T> : holder_val : Ttype()返回typeid(T)clone()深拷贝当前值
Any数据_content : holder*
- 接口
- 默认构造:空指针
- 模板构造:包装任意
T - 拷贝构造:调用虚函数
clone - 析构:删除
_content swapGet<T>()- 用
typeid断言类型一致 - 返回内部值指针
- 用
- 模板赋值与 Any 赋值
- copy-and-swap
- HTTP 中的具体关系
Connection::_context : AnyHttpServer::OnConnected写入HttpContextHttpServer::OnMessage用Get<HttpContext>()取回- 每条 TCP 连接独享自己的 HTTP 增量解析状态
- 协议升级关系
Connection::Upgrade- 同时替换
Any上下文与四类业务回调
- 定位:让
-
🔗 连接对象
Connection·server.hpp- 继承
enable_shared_from_this<Connection>- 回调期间通过
shared_from_this()保持自身存活
- 核心数据
- 身份与状态
_conn_id : uint64_t_sockfd : int_statu : ConnStatu_enable_inactive_release : bool
- 所属执行器
_loop : EventLoop*
- 组合对象
_socket : Socket_channel : Channel_in_buffer : Buffer_out_buffer : Buffer_context : Any
- 身份与状态
- 回调类型
ConnectedCallBack(conn)MessageCallBack(conn, Buffer*)ClosedCallBack(conn)AnyEventCallBack(conn)
- 回调数据
- 应用层
_connected_callback_message_callback_closed_callback_event_callback
- 框架内部
_server_closed_callback- 指向
TcpServer::RemoveConnection
- 应用层
- 构造时绑定 Channel
- read →
HandleRead - write →
HandleWrite - error →
HandleError - close →
HandleClose - any event →
HandleEvent
- read →
- 私有 I/O 回调
HandleRead:socket → 输入 Buffer → 消息回调HandleWrite:输出 Buffer → socket → 清空后停写监控HandleClose:处理剩余输入 → ReleaseHandleError→HandleCloseHandleEvent:刷新定时器 → 应用任意事件回调
- InLoop 生命周期接口
EstablishedInLoopReleaseInLoopSendInLoopShutdownInLoopEnableInactiveReleaseInLoopCancelInactiveReleaseInLoopUpgradeInLoop
- 对外接口
- 查询
FdIdConnected
- 上下文
SetContextGetcontext
- 回调注入
SetConnectedCallBackSetMessageCallBackSetClosedCallBackSetAnyEventCallBackSetSrvClosedCallBack
- 生命周期
EstablishedSendShutDownReleaseEnableInactiveReleaseCancelInactiveReleaseUpgrade
- 查询
- 继承
-
🚦 连接状态
ConnStatuCONNECTINGConnection已创建但 Channel 尚未正式开始通信
CONNECTEDEstablishedInLoop完成- 已启用读监控,可正常收发
DISCONNECTING- 已请求关闭
- 等待剩余输入处理和输出发送
DISCONNECTED- 已移除事件监控并关闭 socket
- 主要转换
CONNECTING- ↓
EstablishedInLoop
- ↓
CONNECTED- ↓
ShutdownInLoop
- ↓
DISCONNECTING- ↓ 输出缓冲清空
DISCONNECTED
- 异常或超时路径
CONNECTED→Release→DISCONNECTED
-
👂 监听器
Acceptor·server.hpp- 数据
_socket : Socket_loop : EventLoop*_channel : Channel_accept_callback : function<void(int)>
- 构造
CreateServer(port)获得监听 fd- Channel 绑定 Main Reactor 与监听 fd
- 读回调绑定
HandleRead
- 接口
SetAcceptCallBackLinst:使能读监控
- 新连接路径
HandleRead- ↓
_socket.Accept() newfd- ↓
_accept_callback TcpServer::NewConnection
- 数据
-
🏢 TCP 服务器
TcpServer·server.hpp- 定位:底层网络框架的总装配器与连接拥有者
- 数据
_next_id : uint64_t_port : int_timeout : int_enable_inactive_release : bool_baseloop : EventLoop_acceptor : Acceptor_pool : LoopThreadPool_conns : unordered_map<uint64_t, PtrConnection>
- 应用回调
_connected_callback_message_callback_closed_callback_event_callback
- 私有接口
RunAfterInLoop(task, delay)- 生成 id
- 在主 EventLoop 时间轮添加任务
NewConnection(fd)- 生成连接 id
- 选择 Sub Reactor
- 创建 Connection
- 注入应用回调和服务器关闭回调
- 启用可选超时
- Established
- 存入
_conns
RemoveConnection(conn)- 把删除动作投回主 EventLoop
RemoveConnectionInLoop(conn)- 从
_conns删除
- 从
- 对外接口
SetThreadCount- 四类回调 setter
EnableInactiveReleaseRunAfterStart
- 主从 Reactor 职责
- Main Reactor
- 接收新连接
- 管理全局连接表
- 执行服务器级定时任务
- Sub Reactor
- 管理被分配的 Connection
- 执行客户端 I/O 与业务回调
- Main Reactor
-
🛡️ 全局初始化与日志 ·
server.hpp- 日志等级
INF = 0DBG = 1ERR = 2- 默认门槛
DEFAULT_LOG_LEVEL = DBG
LOG(level, format, ...)- 检查等级
- 格式化当前时间
- 输出线程 id、时间、文件、行号、消息
- 便捷宏
INF_LOGDBG_LOGERR_LOG
NetWork- 构造时调用
signal(SIGPIPE, SIG_IGN) - 避免向已关闭连接写数据时信号终止进程
- 构造时调用
- 全局静态对象
nw- 在
main前构造 - 自动完成 SIGPIPE 初始化
- 在
- 日志等级
-
🧰 HTTP 工具类
Util·http/http.hpp- 全局数据表
_statu_msg : unordered_map<int, string>- HTTP 状态码 → 原因短语
_mime_msg : unordered_map<string, string>- 扩展名 → MIME 类型
- 字符串
Split(src, sep, array*)- 查询字符串与路径分段共用
- 文件
ReadFile(filename, string*)- 二进制读取整个文件
- 被静态资源处理调用
WriteFile(filename, string)- 二进制覆盖写入
- 被 PUT 示例调用
- URL
UrlEncode(url, convert_space_to_plus)- 非保留字符保持原样
- 查询字符串空格可转
+ - 其他字符转
%HH
HEXTOT(c)- 十六进制字符转 0–15
UrlDecode(url, convert_plus_to_space)%HH解码- 查询字符串中的
+可转空格
- HTTP 元数据
StatuDesc(statu)ExtMime(filename)
- 文件系统判断
IsDirectoryIsRegular- 均基于
stat
- 路径约束
VaildPath(path)- 按
/分段 - 用层级计数阻止路径越出静态根目录
- 全局数据表
-
📥 HTTP 请求
HttpRequest·http/http.hpp- 数据字段
_method : string_path : string_version : string_body : string_matches : smatch:路由正则捕获结果_headers : unordered_map<string, string>_params : unordered_map<string, string>
- 默认值
_version = "HTTP/1.1"
- 生命周期
ReSet清空本次请求并恢复默认版本
- Header 接口
SetHeaderHasHeaderGetHandler
- Query Param 接口
SetParamHasParamGetParam
- 协议辅助
ContentLength- 从
Content-Length头转为整数
- 从
Close- 显式
Connection: keep-alive才判定为长连接
- 显式
- 数据生产者
HttpContext::ParseHttpLineHttpContext::ParseHttpHeadHttpContext::RecvHttpBody
- 数据消费者
HttpServer::Route- 静态文件处理
- 应用层
Handler WriteResponse
- 数据字段
-
📤 HTTP 响应
HttpResponse·http/http.hpp- 数据字段
_statu : int_redirect_flag : bool_body : string_redirect_url : string_headers : unordered_map<string, string>
- 默认值
- 状态码 200
- 非重定向
- 接口
ReSetSetHeaderHasHeaderGetHeaderSetContent(body, type)- 写正文
- 写 Content-Type
SetRedirect(url, statu=302)- 写状态码、重定向标志和 URL
Close- 根据响应
Connection头判断是否关闭
- 根据响应
- 数据生产者
- 应用 Handler
FileHandlerErrorHandler
- 数据消费者
HttpServer::WriteResponse
- 数据字段
-
🧩 HTTP 增量解析状态机
HttpContext·http/http.hppHttpRecvStatuRECV_HTTP_ERRORRECV_HTTP_LINERECV_HTTP_HEADRECV_HTTP_BODYRECV_HTTP_OVER
- 数据
_resp_statu : int_recv_statu : HttpRecvStatu_request : HttpRequest
- 初始状态
- 响应状态 200
- 接收状态
RECV_HTTP_LINE
- 请求行
ParseHttpLine- 正则识别
- 方法:GET/HEAD/POST/PUT/DELETE
- 路径
- 可选查询字符串
- HTTP/1.0 或 HTTP/1.1
- 方法统一转大写
- 路径 URL 解码但不把
+转空格 - 查询串先按
&分割 - 每项再按
=分键值 - 参数 URL 解码并把
+转空格 - 格式错误 → 状态 400
- 正则识别
- 请求行接收
RecvHttpLine- 从
Buffer::GetLineAndPop取一行 - 数据不全 → 保持当前状态
- 超过 8192 字节 → 414
- 成功 →
RECV_HTTP_HEAD
- 从
- Header
ParseHttpHead- 去掉 CRLF
- 按
": "分割 key/value - 写入
HttpRequest::_headers - 格式错误 → 400
- Header 接收
RecvHttpHead- 循环取行
- 空行代表头部结束
- 数据不全则等待
- 超限 → 414
- 完成 →
RECV_HTTP_BODY
- Body 接收
RecvHttpBody- 根据
Content-Length - 无正文 →
RECV_HTTP_OVER - 数据足够 → 取剩余长度并完成
- 数据不足 → 全部追加后等待下一批
- 根据
- 总入口
RecvHttpRequest- switch 故意连续落入
- 同一次调用可依次处理请求行、头部、正文
- 对外查询
RespStatuRecvStatuRequest
ReSet- 响应状态回到 200
- 接收状态回到请求行
- 重置内部
HttpRequest
-
🚀 HTTP 服务器
HttpServer·http/http.hpp- 定位:把 HTTP 协议、路由和静态文件能力接到
TcpServer - 路由类型
Handler = function<void(const HttpRequest&, HttpResponse*)>Handlers = vector<pair<regex, Handler>>
- 路由表
_get_route_post_route_put_route_delete_route
- 组合数据
_basedir : string_server : TcpServer
- 连接回调
OnConnected- 向
Connection::Any写入新的HttpContext
- 向
OnMessage- 从 Any 取得当前连接的
HttpContext - 循环消费
Buffer - 未完成 → 等待后续数据
- 解析错误 → 错误页 + 响应 + 关闭
- 解析完成 → 路由 + 响应 + 重置
- 从 Any 取得当前连接的
- 静态文件
SetBaseDir- 断言路径是目录
IsFileHandler- 要求已配置根目录
- 方法为 GET/HEAD
- 路径合法
- 对应普通文件存在
- 路径以
/结尾时补index.html
FileHandlerUtil::ReadFileUtil::ExtMime- 填充响应体和 Content-Type
- 功能路由
Get / Post / Put / Delete- pattern 编译为
regex - 与 Handler 一起追加到对应路由表
- pattern 编译为
Dispatcher- 顺序尝试
regex_match - 捕获数据写入
req._matches - 命中即调用 Handler
- 全部未命中 → 404
- 顺序尝试
Route- 静态文件优先
- 然后按方法选择路由表
- 不支持的方法 → 405
- 响应
ErrorHandler- 用状态码和
Util::StatuDesc生成 HTML
- 用状态码和
WriteResponse- 完善响应头
- 序列化 HTTP 报文
- 调用
Connection::Send
- 服务器配置
- 构造
(port, timeout=10)- 构造底层
TcpServer - 开启非活跃连接回收
- 注册 OnConnected 和 OnMessage
- 构造底层
SetThreadCount→TcpServer::SetThreadCountListen→TcpServer::Start
- 构造
- 定位:把 HTTP 协议、路由和静态文件能力接到
-
🖥️ HTTP 示例应用 ·
http/main.cc- 常量
WWWROOT = "./wwwroot/"
- 辅助函数
RequestStr(req)- 拼接请求方法、路径、版本
- 遍历查询参数
- 遍历请求头
- 追加请求正文
- 返回便于观察的纯文本
- 路由处理器
Hello(req, rsp)- 用
RequestStr回显请求 - Content-Type 为
text/plain
- 用
Login(req, rsp)- 用
RequestStr回显请求 - Content-Type 为
text/plain
- 用
PutFile(req, rsp)- 路径:
WWWROOT + req._path Util::WriteFile(pathname, req._body)
- 路径:
DelFile(req, rsp)- 当前实现回显请求
main- 创建
HttpServer(8085) SetThreadCount(3)SetBaseDir("./wwwroot/")- 注册
- GET
/hello→Hello - POST
/login→Login - PUT
/1234.txt→PutFile - DELETE
/1234.txt→DelFile
- GET
Listen
- 创建
- 静态资源
http/wwwrootindex.html- 表单 action
/login - method
post - 用户名输入框
- 密码输入框
- 提交按钮
- 浏览器访问
/时由静态文件分支返回
- 表单 action
1234.txt- 可通过 GET 静态读取
- 可通过 PUT 路由覆盖正文
- 常量
-
🔄 HTTP 示例中的具体请求路径
GET /Route- ↓ 静态文件优先
IsFileHandler- ↓
/补index.html FileHandler- ↓
Util::ReadFile - 返回登录表单 HTML
POST /loginHttpContext按 Content-Length 收集表单正文Route选择_post_routeDispatcher命中/loginLoginRequestStr- 返回纯文本请求详情
GET /hello?name=abcParseHttpLine_path = "/hello"_params["name"] = "abc"_get_route命中Hello- 返回纯文本请求详情
PUT /1234.txt_put_route命中PutFile- 请求正文写入
./wwwroot/1234.txt
GET /1234.txt- 静态文件优先于功能路由
- 返回文本文件
DELETE /1234.txt_delete_route命中DelFile- 返回纯文本请求详情
-
🔀 关键回调关系总览
- 事件级回调
Channel保存无参回调- fd 就绪 →
Channel::HandleEvent
- 连接级回调
Connection把 Channel 回调转换为带PtrConnection的业务回调- read →
MessageCallBack(conn, Buffer*) - connected/closed/any-event → 对应连接回调
- 服务器级回调
TcpServer接收上层注册的回调- 新建 Connection 时复制给连接对象
- 应用级回调
- HTTP
TcpServer → HttpServer::OnConnectedTcpServer → HttpServer::OnMessage
- HTTP
- HTTP 路由回调
HttpServer::Dispatcher- ↓
Handler(const HttpRequest&, HttpResponse*) Hello / Login / PutFile / DelFile
- 事件级回调
-
🧠 关键数据结构转换
- TCP 接收
- 内核 socket 接收缓冲区
- ↓
Socket::NonBlockRecv - 栈上
char[65536] - ↓
Buffer::WriteAndPush Connection::_in_buffer
- HTTP 解析
- Buffer 字节
- ↓
HttpContext HttpRequest- method
- path
- version
- headers
- params
- body
- regex matches
- HTTP 路由
HttpRequest::_method- ↓ 选择
Handlers HttpRequest::_path- ↓
regex_match Handler- ↓ 写入
HttpResponse
- HTTP 序列化
HttpResponse- ↓
WriteResponse - HTTP 状态行 + headers + CRLF + body
- ↓
Connection::Send - 临时 Buffer
- ↓
SendInLoop Connection::_out_buffer- ↓
HandleWrite - socket
- 连接上下文
HttpContext- ↓
Any::placeholder<HttpContext> Connection::_context- ↓
Any::Get<HttpContext> - 下一批 TCP 数据继续同一解析状态
- TCP 接收
-
🧵 线程与数据归属
- 主线程 Main Reactor
TcpServer::_baseloopAcceptor- 监听 fd
_conns连接表的增删协调
- Sub Reactor 线程
- 每个
LoopThread一个EventLoop - 每个 Connection 固定归属一个 EventLoop
- 该连接的 Channel、Buffer、上下文和 I/O 回调在所属线程串行处理
- 每个
- 跨线程规则
- 普通操作 →
EventLoop::RunInLoop - 当前就是目标线程 → 立即执行
- 否则 → 任务队列 + eventfd 唤醒
- 普通操作 →
- 典型跨线程动作
- 主线程把新 Connection 的 Established 投给 Sub Reactor
- 任意线程调用 Connection::Send
- Sub Reactor 关闭 Connection 后把 map 删除投回 Main Reactor
- 定时器增删刷新投给定时轮所属 EventLoop
- 共享与生命周期
_conns保存shared_ptr<Connection>- 回调参数也使用
shared_ptr enable_shared_from_this防止回调中对象提前释放- TimerWheel 用 shared_ptr 决定任务何时析构执行
_timers用 weak_ptr 查询而不延长任务寿命
- 主线程 Main Reactor
-
📁 文件与构建关系
source/server.hpp- 头文件内实现完整 TCP/Reactor 框架
- 被 HTTP 示例间接包含
source/httphttp.hpp- 包含
../server.hpp - 定义 HTTP 工具、报文、状态机、服务器
- 包含
main.cc- 包含
http.hpp - 注册示例路由并启动 8085 端口
- 包含
makefileg++ -g -std=c++11 main.cc -o main -lpthread
main- 已生成的可执行文件
wwwroot/index.html- 登录表单静态首页
wwwroot/1234.txt- 静态文本与 PUT 示例目标
-
🗺️ 从文件定位类
server.hppBufferSocketChannelPollerTimerTaskTimerWheelEventLoopLoopThreadLoopThreadPoolAnyholderplaceholder<T>
ConnStatuConnectionAcceptorTcpServerNetWork
http/http.hppUtilHttpRequestHttpResponseHttpRecvStatuHttpContextHttpServer
http/main.ccRequestStrHelloLoginPutFileDelFile
-
🧭 快速回忆口诀
- 一条连接
- Accept → 分配 Loop → Connection → Channel → 回调
- 一次读
- epoll → Poller → Channel → Connection → Buffer → 业务
- 一次写
- 业务 → Send → RunInLoop → 输出 Buffer → EPOLLOUT → socket
- 一次 HTTP
- Buffer → HttpContext → HttpRequest → Route → Handler → HttpResponse → Send
- 多线程
- Main Reactor 接连接,Sub Reactor 管读写
- 跨线程
- 任务进队列,eventfd 唤醒目标 Loop
- 超时回收
- timerfd 驱动时间轮,连接事件刷新任务,超时触发 Release
- 生命周期
_conns持有 shared_ptr,关闭后回主 Loop 删除
- 核心主线
- 应用层 → HTTP → TcpServer → Connection → EventLoop → Poller → epoll
- 一条连接
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
相关文章智能推荐
1
My_server框架全流程梳理
项目梳理先看 EventLoop::Start()——整个框架的一切活动都发生在这个循环里,而它的三步是互锁的因果链:
2
流程梳理
项目梳理先看 EventLoop::Start()——整个框架的一切活动都发生在这个循环里
3
HTTP
项目梳理整个代码实现了一个经典的多进程并发 HTTP 静态 Web 服务器。它的核心思想是将网络传输层(TCP)和应用层(HTTP)解耦。
4
搜索引擎思维导图
项目梳理🎯 项目定位 搜索对象:Boost 1.82.0 HTML 文档 核心目标:输入关键词,返回按相关性排序的文档结果 总体模式:离线数据清洗 + 启动时建索引 + 在线查询服务 技术栈 C++11 Boost.Filesystem:递归枚…
5
C++ 搜索引擎 课程笔记
项目梳理LEVEL — 字符串化运算符,LOG(NORMAL, ...) 展开为 log("NORMAL", ...),输出 [NORMAL] 而非 [1] FILE 和 LINE — 编译时硬替换,记录调用位置 四个日志等级常量目前未在 log…
随机文章随机推荐

