从高中开始,我就对 0 和 1 构建出的庞大计算机体系充满震撼——那些冰冷的二进制数字,居然能搭建起如今包罗万象的互联网世界。那时候,我天真地想彻底搞明白“0 和 1 如何构建这一切”,但很快就发现,计算机的复杂度远超我的想象。操作系统、编译器、网络协议、分布式系统……每一层都像一座座高山,初学者几乎无法一口吃下全部知识。
于是,我决定退而求其次,从最基础的网络编程入手:先学 Socket 开发,理解数据如何在网络中传输;再一步步尝试,亲手实现一个属于自己的 Web 服务器。经过几个月的学习与实践,我完成了第一版轻量级 C++ Web 服务器。它基于 Boost.Asio,能够异步监听 TCP 端口、解析 HTTP 请求、返回响应,并通过线程池处理并发连接。为了方便大家学习,我已经将完整代码开源:https://github.com/Fall-Rain/asio_web_service
本篇文章将基于最初提交的代码,从 TCP 服务器搭建、Session 异步请求处理 到 响应发送,带大家了解手写 C++ Web 服务器的基础骨架。
一、TCP 服务器骨架
服务器入口在 main.cpp 中:
boost::asio::io_context io_context;
Server server(io_context, 8090);
server.start();
io_context.run();
server.stop();
流程解析:
- io_context:Boost.Asio 的核心 I/O 对象,用于管理所有异步操作。
- Server:封装了 TCP 端口监听、异步接受连接和线程池分发任务。
- start() / stop():分别用于启动监听和线程池,以及关闭服务器。
- io_context.run():进入事件循环,等待连接、读取和写入操作的回调执行。
Server 的核心:异步接受连接
void Server::do_accept() {
acceptor_.async_accept([this](std::error_code ec, boost::asio::ip::tcp::socket socket) {
if (!ec) {
pool.post([session = std::make_shared<Session>(socket)] {
session->start();
});
}
do_accept();
});
}
解析:
async_accept非阻塞等待客户端连接,连接到来时触发回调- 每个连接创建一个
Session对象,独立处理请求 pool.post()将任务放入线程池,提高并发处理能力- 递归调用
do_accept()实现循环监听下一连接
二、Session:单连接生命周期管理
每个客户端连接对应一个 Session 对象,负责管理从接收请求到发送响应的完整流程。
Session::Session(boost::asio::ip::tcp::socket &socket): client_socket_(std::move(socket)) {}
- 保存 TCP 套接字
client_socket_ - 使用
std::move确保 socket 唯一所有权
异步读取请求
void Session::do_read() {
boost::asio::async_read_until(client_socket_, client_buffer_, "\r\n\r\n",
[self = shared_from_this()](std::error_code ec, std::size_t bytes_transferred) {
if (!ec) {
// 解析请求头
...
self->do_write();
}
}
);
}
async_read_until异步读取,直到遇到 HTTP 报文结束标识\r\n\r\n- 回调中使用
shared_from_this()保证对象在异步回调期间不会被销毁 - 解析请求头,读取请求体(如 POST 请求)
请求头解析逻辑
std::string request_header(boost::asio::buffers_begin(self->client_buffer_.data()),
boost::asio::buffers_begin(self->client_buffer_.data()) + bytes_transferred);
boost::split(headers, request_header, boost::is_any_of("\r\n"), boost::token_compress_on);
- 将请求头按行拆分
- 第一行解析 HTTP 方法、URI、版本
- 其他行解析为键值对存入
header_map
请求体读取
auto content_length = self->header_map.find("Content-Length");
if (content_length != self->header_map.end()) {
...
boost::asio::read(self->client_socket_, boost::asio::buffer(buffer),
boost::asio::transfer_exactly(content_length - self->request_body.size()), error);
...
}
- 判断请求是否有 body
- 根据
Content-Length精确读取请求体
三、发送响应
void Session::do_write() {
std::ostringstream response_stream;
response_stream << "HTTP/1.1 200 OK\r\n";
response_stream << "Content-Type: text/html\r\n";
response_stream << "Content-Length:" << self->request_body.size() << "\r\n";
response_stream << "\r\n";
response_stream << self->request_body; boost::asio::async_write(client_socket_, boost::asio::buffer(response_stream.str()),
[self = shared_from_this()](std::error_code ec, std::size_t length) {
if (ec) std::cerr << "write error: " << ec.message() << std::endl;
});
}
- 将请求体原样返回客户端,便于测试
- 使用
async_write异步发送响应,提高服务器吞吐量
四、线程池支持高并发
ThreadPool::ThreadPool(std::size_t size) : work_(io_context_) {
for (std::size_t i = 0; i < size; ++i) {
threads_.emplace_back([this] { io_context_.run(); });
}
}
- 固定数量线程运行
io_context.run() - 每个线程可以处理异步操作回调,提高并发处理能力
stop()停止所有线程并回收资源
五、小结与开源链接
通过这一版服务器,你可以看到完整的数据流:
客户端 → TCP 套接字 → Session → 请求解析 → 响应 → 客户端
虽然功能简单,但它涵盖了 C++ Web 服务器的核心概念:
- 异步 TCP 监听
- Session 生命周期管理
- HTTP 请求头解析与请求体读取
- 异步响应发送
- 线程池并发处理
这套服务器代码已经开源,大家可以在 GitHub 上直接下载、编译和调试:https://github.com/Fall-Rain/asio_web_service
在后续文章中,我会讲解如何进一步封装路由、添加中间件和 WebSocket 支持,让服务器逐步从基础骨架成长为可实战使用的轻量级 C++ Web 框架。
文章评论