HTTP
1096 字
5 分钟
HTTP
整个代码实现了一个经典的多进程并发 HTTP 静态 Web 服务器。它的核心思想是将**网络传输层(TCP)和应用层(HTTP)**解耦。
代码的运行逻辑可以划分为五个主要阶段,我们直接从程序的入口 main 函数开始梳理:
1. 启动与初始化阶段 (Main.cc -> Http.hpp -> TcpServer.hpp)
一切从 [Main.cc](code-assist-path:d:\HuaweiMoveData\Users\HUAWEI\Desktop\http\Main.cc "d:\HuaweiMoveData\Users\HUAWEI\Desktop\http\Main.cc") 中的 main 函数开始:
- 获取端口号:程序启动时从命令行读取用户指定的端口号(例如
./server 8080)。 - 实例化 Http 服务器:调用
std::make_unique<Http>(port)创建 HTTP 服务器对象。- 在
Http的构造函数中,它会立刻创建一个底层的TcpServer对象。 TcpServer被创建时,会初始化自己的_listensockptr(一个TcpSocket对象),并调用BuildTcpSocketMethod(port),完成底层 TCP 的三大步:socket()创建套接字、bind()绑定端口、listen()进入监听状态。
- 在
- 启动服务器:调用
httpsvr->Start()。
2. 注入回调与主循环 (Http.hpp -> TcpServer.hpp)
当调用 Http::Start() 时:
- 它是通过调用底层
tsvrp->Start(...)真正跑起来的,并且传入了一个 Lambda 表达式。这个 Lambda 实际上把Http::HandlerHttpRquest打包成了一个回调函数(callback)。这就做到了网络层不知道应用层在干嘛,只负责把收到的连接扔给回调。 - 随后程序进入
TcpServer::Start的while(_isrunning)死循环中。 - 阻塞等待:主线程在这个循环里调用
_listensockptr->Accept(&client),程序会在这里挂起,直到有浏览器(客户端)发起 TCP 连接。
3. 多进程并发处理 (TcpServer.hpp)
当一个浏览器连接进来后,Accept 成功返回一个专门用于和这个客户端通信的通信套接字 sock:
- Fork 子进程:
TcpServer调用fork()创建子进程。 - 孙子进程机制(孤儿进程):为了防止产生僵尸进程,这里用了一个非常经典的 Linux 系统编程技巧:
- 父进程直接
waitpid回收子进程。 - 子进程里面再
fork()一次产生孙子进程,然后子进程立刻exit()退出。 - 孙子进程因为失去了父进程,会被系统的
init(或systemd) 进程接管。
- 父进程直接
- 执行任务:这个孙子进程负责执行之前传进来的回调函数
callback(sock, client),也就是去处理具体的 HTTP 逻辑,处理完后自动关闭 socket 并退出。父进程则毫无负担地继续循环Accept下一个连接。
4. HTTP 报文处理与响应 (Http.hpp -> Util.hpp)
逻辑现在来到了孙子进程执行的 Http::HandlerHttpRquest 函数:
- 读取请求:通过
sock->Recv(&httpreqstr)从 TCP 接收缓冲区把浏览器发来的 HTTP 请求报文(一段纯字符串)读出来。 - 解析请求 (Request):
- 实例化
HttpRequest并调用Deserialize进行反序列化。 - 利用
Util::ReadOneLine提取出第一行(请求行),并用stringstream分割出方法(如GET)、URI(如/或/image.jpg)和协议版本。 - 将请求的 URI 映射到本地磁盘的
./wwwroot目录下(例如/被转换为[./wwwroot/index.html](code-assist-path:d:\HuaweiMoveData\Users\HUAWEI\Desktop\http\wwwroot\index.html "d:\HuaweiMoveData\Users\HUAWEI\Desktop\http\wwwroot\index.html"))。
- 实例化
- 构建响应 (Response):
- 实例化
HttpResponse,把刚才映射好的文件路径喂给它。 - 调用
MakeResponse():底层通过Util::ReadFileContent去读取磁盘文件。如果文件存在,状态码设为200 OK并附上Content-Length头;如果文件不存在,状态码设为404 Not Found并返回一段写死的错误 HTML 页面。
- 实例化
- 序列化与发送:
- 调用
HttpResponse::Serialize(),将构建好的响应对象按照 HTTP 协议标准拼装成一段长字符串:状态行 + 响应头 + 空行 + 网页正文。 - 调用
sock->Send(response_str)将这段字符串通过 TCP 发送回浏览器。
- 调用
5. 日志贯穿始终 (Log.hpp)
在上述所有流程的运行过程中,你会发现穿插着大量的 LOG(LogLevel::xxx) << "...":
- 这得益于
[Log.hpp](code-assist-path:d:\HuaweiMoveData\Users\HUAWEI\Desktop\http\Log.hpp "d:\HuaweiMoveData\Users\HUAWEI\Desktop\http\Log.hpp")中巧妙的重载operator<<机制。 - 只要发生重要的状态变更(如 Bind 成功、Accept 成功、甚至发生错误退出),它都会在内存中利用
stringstream将时间戳、日志等级、进程 ID、文件名和行号拼装成一行日志。 - 并在
LogMessage对象析构时(即这一行代码执行完毕离开作用域时),触发写入策略,将日志输出到控制台或写入/var/log/my.log中。
总体来说,整个架构非常清晰:Main 驱动,TcpServer 负责并发和网络 I/O,Http 层负责应用层协议编解码,Util 辅助文件操作,Log 负责监控运行状态。
Generated by Gemini 3.1 Pro Preview
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
相关文章智能推荐
1
My_server框架全流程梳理
项目梳理先看 EventLoop::Start()——整个框架的一切活动都发生在这个循环里,而它的三步是互锁的因果链:
2
流程梳理
项目梳理先看 EventLoop::Start()——整个框架的一切活动都发生在这个循环里
3
My_Server思维导图
项目梳理🎯 项目定位 项目类型:基于 Linux 系统调用实现的 C++11 网络服务器框架 核心模式:Reactor 事件驱动 + 非阻塞 I/O + 主从事件循环 + 回调式业务层 能力边界 底层 TCP 网络通信 epoll I/O 多路…
4
搜索引擎思维导图
项目梳理🎯 项目定位 搜索对象:Boost 1.82.0 HTML 文档 核心目标:输入关键词,返回按相关性排序的文档结果 总体模式:离线数据清洗 + 启动时建索引 + 在线查询服务 技术栈 C++11 Boost.Filesystem:递归枚…
5
C++ 搜索引擎 课程笔记
项目梳理LEVEL — 字符串化运算符,LOG(NORMAL, ...) 展开为 log("NORMAL", ...),输出 [NORMAL] 而非 [1] FILE 和 LINE — 编译时硬替换,记录调用位置 四个日志等级常量目前未在 log…
随机文章随机推荐

