You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost Beast HTTP服务器处理首次请求后异常退出问题排查

Boost Beast HTTP服务器处理首个请求后异常退出问题

我无法理解为何基于Boost Beast实现的HTTP服务器在处理完第一个请求后就异常退出,甚至未输出“Server has stopped running.”日志便自行终止。目前服务器能正常返回首次请求的响应,但之后便停止运行并退出程序。我已将此实现与官方小型HTTP服务器示例对比,但仍未找出退出原因,二者结构相近,仅服务器类的组织方式不同。

main.cpp

#include <iostream>
#include <unordered_map>
#include <memory>

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>

#include "..\include\server2.hpp"

using namespace std;

constexpr char ADDRESS[] = "0.0.0.0";
constexpr unsigned short PORT = 8080;
constexpr char DOC_ROOT[] = "frontend";

int main(int argc, char* argv[])
{
    try
    {
        auto const address = boost::asio::ip::make_address(ADDRESS);
        unsigned short port = PORT;

        Server server(DOC_ROOT, address, port);
        server.run();
        cout << " hello" << endl;
    }
    catch (std::exception const& e)
    {
        std::cout << "Error: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
}

server2.hpp

#pragma once

#include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <fstream>

#include <boost/asio.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/filesystem.hpp>

#include "toggle_button.hpp"

class Server {
    const boost::asio::ip::address m_address;
    const unsigned short m_port;
    const std::string m_docroot;

    boost::asio::io_context m_ioc;
    boost::asio::ip::tcp::acceptor m_acceptor;

    std::unordered_set<std::shared_ptr<boost::asio::ip::tcp::socket>> sse_clients;

    int i;
public:
    Server(const std::string& docroot, const boost::asio::ip::address& address, const unsigned short& port) :
        m_docroot(docroot),
        m_address(address),
        m_port(port),
        m_ioc(1),
        m_acceptor(m_ioc, boost::asio::ip::tcp::endpoint(address, port)),
            i(1) {}

    void handle_accept() {
        auto new_socket = std::make_shared<boost::asio::ip::tcp::socket>(m_ioc);

        // Start asynchronous accept operation
        m_acceptor.async_accept(*new_socket, [new_socket, this](boost::system::error_code ec) {
            if (!ec) {
                // Start a new session with the accepted socket
                handle_session(std::move(new_socket));
            }
            else {
                std::cerr << "Accept error: " << ec.message() << std::endl;
            }

            // Keep accepting new connections
            std::cout << " session << " << i++ << std::endl;
            handle_accept();
            });
    }

    void run() {
        std::cout << "Server is running and accepting connections..." << std::endl;
        // Start accepting connections
        handle_accept();

        // Now run the io_context
        m_ioc.run();
        std::cout << "Server has stopped running." << std::endl;
    }

private:
    void handle_session(std::shared_ptr<boost::asio::ip::tcp::socket> socket) {
        auto buffer = std::make_shared<boost::beast::flat_buffer>(100000);
        auto request = std::make_shared<boost::beast::http::request<boost::beast::http::string_body>>();

        boost::beast::http::async_read(*socket, *buffer, *request,
            [this, socket, buffer, request](boost::system::error_code ec, std::size_t bytes_transferred) mutable {
                if (!ec) {
                    boost::beast::http::response<boost::beast::http::dynamic_body> response;
                    response.result(boost::beast::http::status::ok);
                    response.set(boost::beast::http::field::content_type, "text/html");
                    response.version(request->version());
                    response.keep_alive(false);
                    response.set(boost::beast::http::field::server, "Beast");
                    boost::beast::ostream(response.body())
                        << "<html>
"
                        << "<head><title>Hello</title></head>
"
                        << "<body>
"
                        << "<h1>Hello world</h1>
"
                        << "<p>It's just me</p>
"
                        << "</body>
"
                        << "</html>
";

                    response.prepare_payload();
                    
                    boost::beast::http::async_write(
                        *socket,
                        response,
                        [socket](boost::beast::error_code ec, std::size_t)
                        {
                            (*socket).shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec);
                        });
                }
                else {
                    std::cerr << "Read error: " << ec.message() << std::endl;
                }
            });
    }
};

问题根源

核心问题在于**io_context在所有异步操作完成后会自动停止运行**:

  • 第一个请求的异步读、写操作完成后,socket被关闭,此时没有其他挂起的异步任务,io_context.run()会直接返回,程序继续执行后续代码并退出,因此不会输出“Server has stopped running.”日志。
  • 虽然handle_accept中调用了自身来维持accept循环,但当io_context的任务队列耗尽时,后续的异步accept操作无法被调度。

修复方案

  1. 添加executor_work_guard维持io_context运行
    在Server类中添加work guard成员,阻止io_context在无任务时停止:
// Server类成员添加
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> m_work;

// 构造函数初始化
Server(const std::string& docroot, const boost::asio::ip::address& address, const unsigned short& port) :
    m_docroot(docroot),
    m_address(address),
    m_port(port),
    m_ioc(1),
    m_work(boost::asio::make_work_guard(m_ioc)), // 初始化work guard
    m_acceptor(m_ioc, boost::asio::ip::tcp::endpoint(address, port)),
    i(1) {}
  1. 优化socket回调中的资源管理
    在async_write的回调中忽略shutdown的无关错误,让智能指针自动管理socket生命周期:
boost::beast::http::async_write(
    *socket,
    response,
    [socket](boost::beast::error_code ec, std::size_t)
    {
        if (!ec) {
            boost::system::error_code ignore_ec;
            socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send, ignore_ec);
        }
    });
  1. 确保accept循环持续触发
    当前handle_accept的实现已经在每次accept后递归调用自身,保持这个逻辑即可,即使出现accept错误也不会中断循环。

内容的提问来源于stack exchange,提问作者Jon Castillo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 20:22:13