【C++网络编程】番外篇(实战):基于Boost.Asio协程的HTTP服务器实现与静态文件服务开发指南

一、引言:协程化网络编程的优势

在传统的同步IO模型中,每个连接需要独占线程资源,当面对高并发场景时会产生巨大的线程切换开销。Boost.Asio的协程支持通过以下方式解决这个问题:

  1. 无栈协程(Stackless Coroutine)​
    基于C++20协程TS实现,每个协程仅需约1KB内存
  2. ​协作式调度
    通过co_await关键字实现非阻塞等待,避免线程切换
  3. 天然异步集成
    与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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JuicyActiveGilbert

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值