一、引言:协程化网络编程的优势
在传统的同步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: 转换method
req.method = std::string(msg.method_string());
// 2: 转换target路径
req.path = std::string(msg.target());
// 3: 转换headers
for (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); // 设置状态码200
res.set(http::field::content_type, "application/json"); // 内容类型
res.body() = data.dump(); // JSON序列化
res.content_length(res.body().size()); // 显式设置内容长度
co_await http::async_write(socket