【C++网络编程】番外篇(实战):基于Boost.Asio协程的HTTP服务器实现与静态文件服务开发指南
一、引言:协程化网络编程的优势
在传统的同步IO模型中,每个连接需要独占线程资源,当面对高并发场景时会产生巨大的线程切换开销。Boost.Asio的协程支持通过以下方式解决这个问题:
- 无栈协程(Stackless Coroutine)
基于C++20协程TS实现,每个协程仅需约1KB内存 - 协作式调度
通过co_await关键字实现非阻塞等待,避免线程切换 - 天然异步集成
与Asio的异步IO模型完美结合,实现高效资源利用
二、核心实现:HTTP协议解析器
1. HTTP请求结构体定义
struct http_request {std::string method; // 请求方法(GET/POST等)std::string path; // 请求路径(如"/index.html")std::map<std::string, std::string> headers; // 请求头键值对std::string body; // 请求体内容
};
2. 协程化请求解析函数
asio::awaitable<http_request> parse_http_request(tcp::socket& socket) {http_request req; // 存储解析结果beast::flat_buffer buffer; // 高效二进制缓冲区http::request_parser<http::dynamic_body> parser; // 动态body解析器// 关键步骤1:异步读取请求头co_await http::async_read_header(socket, buffer, parser, asio::use_awaitable);auto& msg = parser.get(); // 获取解析后的消息对象
3. 请求方法解析
// 将boost::string_view转换为std::string
req.method = std::string(msg.method_string());
- 方法来源:
method_string()
返回boost::beast::string_view
- 转换必要性:避免原始数据被缓冲区释放后失效
- 支持方法:自动处理
GET/POST/PUT/DELETE
等标准方法
4. 请求路径处理
req.path = std::string(msg.target()); // 原始请求路径
- 示例输入:/api/status
- 注意点:此时路径包含原始查询字符串,需后续处理
- 安全性:需要后续的路径消毒(sanitize_path)
5. 请求头解析
for (auto const& field : msg) {std::string name = std::string(field.name_string());std::string value = std::string(field.value());req.headers[name] = value;
}
- 遍历机制:msg实现了begin/end迭代器
- 头字段示例:
a)Content-Type: application/json
b)Authorization: Bearer token
- 大小写处理:
Boost.Beast
自动转换为小写键名
6. 请求体处理
if (parser.content_length().value_or(0) > 0) {co_await http::async_read(socket, buffer, parser, asio::use_awaitable);req.body = beast::buffers_to_string(parser.get().body().data());
}
- 条件判断:仅当存在有效
Content-Length
时读取body - 异步读取:继续从
socket
读取剩余数据 - 数据转换:将多个
buffer
片段拼接为完整字符串 - 性能考量:对于大文件应考虑流式处理
7. 完整代码
//http_parser.hpp
#pragma once
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <sstream>
#include <map>namespace asio = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
using tcp = asio::ip::tcp;struct http_request {std::string method;std::string path;std::map<std::string, std::string> headers;std::string body;
};asio::awaitable<http_request> parse_http_request(tcp::socket& socket) {http_request req;beast::flat_buffer buffer;http::request_parser<http::dynamic_body> parser;// 读取请求头co_await http::async_read_header(socket, buffer, parser, asio::use_awaitable);auto& msg = parser.get();// 1: 转换methodreq.method = std::string(msg.method_string());// 2: 转换target路径req.path = std::string(msg.target());// 3: 转换headersfor (auto const& field : msg) {std::string name = std::string(field.name_string());std::string value = std::string(field.value());req.headers[name] = value;}// 4: 转换body内容if (parser.content_length().value_or(0) > 0) {co_await http::async_read(socket, buffer, parser, asio::use_awaitable);req.body = beast::buffers_to_string(parser.get().body().data());}co_return req;
}
三、通用工具类
1. 通用响应函数
asio::awaitable<void> send_response(tcp::socket& socket,http::status status,const std::string& body = "") {http::response<http::string_body> res;res.result(status);res.set(http::field::server, "AsioHTTP/1.0");res.body() = body;res.content_length(body.size());co_await http::async_write(socket, res, asio::use_awaitable);
}
应用场景:
- 发送错误响应(404/500等)
- 返回简单文本信息
- 调试信息输出
2. 完整代码
//utils.hpp
#pragma once
#include <boost/asio.hpp>asio::awaitable<void> send_response(tcp::socket& socket,http::status status,const std::string& body = "")
{http::response<http::string_body> res;res.result(status);res.set(http::field::server, "AsioHTTP/1.0");res.body() = body;res.content_length(body.size());co_await http::async_write(socket, res, asio::use_awaitable);
}
四、API处理类
1. send_json
函数
asio::awaitable<void> send_json(tcp::socket& socket, const json& data) {http::response<http::string_body> res;res.result(http::status::ok); // 设置状态码200res.set(http::field::content_type, "application/json"); // 内容类型res.body() = data.dump(); // JSON序列化res.content_length(res.body().size()); // 显式设置内容长度co_await http::async_write