在改编的Boost Beast async-ssl WebSocket示例中未初始化(SSL例程)

o75abkj4  于 2023-06-06  发布在  其他
关注(0)|答案(1)|浏览(124)

我正在尝试调整boost beast的websocket/server/async-ssl/websocket_server_async_ssl.cpp,以动态刷新index.html要查看的html表格。我为服务器main.cpp编写的代码编译并运行在0.0.0.0:8080上,但是当浏览器查看html文件时,服务器给出一个错误消息accept: uninitialized (SSL routines)。我用的是boost v1.81.0

main.cpp
//------------------------------------------------------------------------------
//
// Example: WebSocket server, asynchronous with SSL/TLS encryption
//
//------------------------------------------------------------------------------

#include <boost/beast/core.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/strand.hpp>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include <sstream>
#include <chrono>
#include "utils.hpp"

// Generate the HTML table content with random values
std::string generate_html_table()
{
    std::stringstream ss;
    // Generate a random number of rows and columns
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(1, 10);
    int rows = dis(gen);
    int cols = dis(gen);

    // Generate a random HTML table with random values
    ss << "<table>\n";
    for (int i = 0; i < rows; i++) {
        ss << "<tr>\n";
        for (int j = 0; j < cols; j++) {
            std::uniform_int_distribution<> dis(1, 100);
            int value = dis(gen);
            ss << "<td style=\"background-color:rgb(" << value << "," << value << "," << value << ")\">" << value << "</td>\n";
        }
        ss << "</tr>\n";
    }
    ss << "</table>\n";

    return ss.str();
}

namespace beast = boost::beast;         // from <boost/beast.hpp>
namespace http = beast::http;           // from <boost/beast/http.hpp>
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
namespace net = boost::asio;            // from <boost/asio.hpp>
using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>
namespace ssl = boost::asio::ssl;       // from <boost/asio/ssl.hpp>

//------------------------------------------------------------------------------

// Report a failure
void fail(beast::error_code ec, char const* what)
{
    std::cerr << what << ": " << ec.message() << "\n";
}

// Echoes back all received WebSocket messages
class session : public std::enable_shared_from_this<session>
{
    websocket::stream<beast::ssl_stream<beast::tcp_stream>> ws_;
    beast::flat_buffer buffer_;

public:
    // Take ownership of the socket
    explicit session(tcp::socket&& socket, ssl::context& ctx)
            : ws_(std::move(socket), ctx)
    {
    }

// Get on the correct executor
    void run()
    {
        // We need to be executing within a strand to perform async operations
        // on the I/O objects in this session. Although not strictly necessary
        // for single-threaded contexts, this example code is written to be
        // thread-safe by default.
        net::dispatch(ws_.get_executor(),
                      beast::bind_front_handler(
                              &session::on_run,
                              shared_from_this()));
    }

    // Start the asynchronous operation
    void on_run()
    {
        // Set suggested timeout settings for the websocket
        ws_.set_option(
                websocket::stream_base::timeout::suggested(
                        beast::role_type::server));

        // Set a decorator to change the Server of the handshake
        ws_.set_option(websocket::stream_base::decorator(
                [](websocket::response_type& res)
                {
                    res.set(http::field::server,
                            std::string(BOOST_BEAST_VERSION_STRING) +
                            " websocket-server-async");
                }));

        // Accept the websocket handshake
        ws_.async_accept(
                beast::bind_front_handler(
                        &session::on_accept,
                        shared_from_this()));
    }

    void on_accept(beast::error_code ec)
    {
        if(ec)
            return fail(ec, "accept");

        // Send the colorful HTML table
        send_html_table();
    }

    void send_html_table()
    {
        std::string html;
        for (int i = 0; i < 10; i++) {
            html += generate_html_table(); // Generate the HTML table content
        }

        // Create the WebSocket message with the HTML content
        ws_.text(true);
        ws_.async_write(
                net::buffer(html),
                [self = shared_from_this()](beast::error_code ec, std::size_t bytes_transferred) {
                    if (ec)
                        return fail(ec, "write");

                    // Clear the buffer
                    self->buffer_.consume(self->buffer_.size());

                    // Schedule sending the next HTML table after 1 second
                    self->schedule_next_send();
                });
    }

    void schedule_next_send()
    {
        // Wait for 1 second before sending the next HTML table
        std::this_thread::sleep_for(std::chrono::seconds(1));

        // Check if the socket is still open
        if (ws_.is_open())
        {
            // Send the next HTML table
            send_html_table();
        }
    }
};

//------------------------------------------------------------------------------

// Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener>
{
    net::io_context& ioc_;
    tcp::acceptor acceptor_;
    ssl::context& ctx_;

public:
    listener(
            net::io_context& ioc,
            tcp::endpoint endpoint,
            ssl::context& ctx)
            : ioc_(ioc)
            , acceptor_(ioc)
            , ctx_(ctx)
    {
        beast::error_code ec;

        // Open the acceptor
        acceptor_.open(endpoint.protocol(), ec);
        if(ec)
        {
            fail(ec, "open");
            return;
        }

        // Allow address reuse
        acceptor_.set_option(net::socket_base::reuse_address(true), ec);
        if(ec)
        {
            fail(ec, "set_option");
            return;
        }

        // Bind to the server address
        acceptor_.bind(endpoint, ec);
        if(ec)
        {
            fail(ec, "bind");
            return;
        }

        // Start listening for connections
        acceptor_.listen(
                net::socket_base::max_listen_connections, ec);
        if(ec)
        {
            fail(ec, "listen");
            return;
        }
    }

    // Start accepting incoming connections
    void run()
    {
        do_accept();
    }

private:
    void do_accept()
    {
        acceptor_.async_accept(
                net::make_strand(ioc_),
                beast::bind_front_handler(
                        &listener::on_accept,
                        shared_from_this()));
    }

    void on_accept(beast::error_code ec, tcp::socket socket)
    {
        if(ec)
        {
            fail(ec, "accept");
        }
        else
        {
            std::make_shared<session>(std::move(socket), ctx_)->run();
        }

        // Accept another connection
        do_accept();
    }
};

//------------------------------------------------------------------------------

int main(int argc, char* argv[])
{
    auto const address = net::ip::make_address("0.0.0.0");
    auto const port = static_cast<unsigned short>(8080);
    auto const threads = std::max<int>(1, 1);

    // The io_context is required for all I/O
    net::io_context ioc{threads};

    // The SSL context is required, and holds certificates
    ssl::context ctx{ssl::context::tlsv12};

    // Load certificates
    auto path = path_to_project().string();
    ctx.use_certificate_chain_file(path + "/certificates/server.crt");
    ctx.use_private_key_file(path + "/certificates/server.key", ssl::context::pem);
    ctx.use_tmp_dh_file(path + "/certificates/dh2048.pem");

    // Verify the certificate
    ctx.load_verify_file(path + "/certificates/ca.crt");
    ctx.set_verify_mode(ssl::verify_peer);

    // Create and launch a listening port
    std::make_shared<listener>(ioc, tcp::endpoint{address, port}, ctx)->run();

    // Run the I/O service on the requested number of threads
    std::vector<std::thread> v;
    v.reserve(threads - 1);
    for(auto i = threads - 1; i > 0; --i)
        v.emplace_back(
                [&ioc]
                {
                    ioc.run();
                });
    ioc.run();

    return EXIT_SUCCESS;
}

index.html

<!DOCTYPE html>
<html>
<head>
    <title>Dynamic Table</title>
    <style>
        table {
            border-collapse: collapse;
        }
        td {
            border: 1px solid black;
            padding: 5px;
        }
    </style>
    <script>
        var socket = new WebSocket("ws://localhost:8080"); 

        socket.onmessage = function(event) {
            var tableContainer = document.getElementById("table-container");
            tableContainer.innerHTML = event.data;
        };

        function refreshTable() {
            socket.send("refresh"); // Send a message to the server to request a table refresh
        }

        // Refresh the table every 1 second
        setInterval(refreshTable, 1000);
    </script>
</head>
<body>
<h1>Dynamic Table</h1>
<div id="table-container"></div>
</body>
</html>

我怀疑这个bug是在listener类的on_accept()方法中。不知何故,SSL没有初始化到这一点。

void on_accept(beast::error_code ec, tcp::socket socket)
    {
        if(ec)
        {
            fail(ec, "accept");
        }
        else
        {
            std::make_shared<session>(std::move(socket), ctx_)->run();
        }

        // Accept another connection
        do_accept();
    }

错误发生在fail(ec, "accept")行中。
我该怎么解决这个问题?

2sbarzqh

2sbarzqh1#

从这条线可以看出

res.set(http::field::server, std::string(BOOST_BEAST_VERSION_STRING) + " websocket-server-async");

您的代码实际上(部分)基于示例程序的非SSL版本。这导致了不止一件事的缺失:

  • WebSocket/ssl.hpp包括
  • on_run根本不执行SSL握手x1c 0d1x

你自己有意的改变也有一些问题:

  • send_html_table()写入局部变量的内容,即Undefined Behaviour,因为在async_write完成之前,局部变量已经消失。
  • 同样在async_write的完成处理程序中,您盲目地清除了buffer_,尽管实际上没有读操作使用它
  • schedule_next_send()做了与它所承诺的相反的事情。它不进行调度,而是阻塞整个世界--这意味着链上的任何东西至少都无法取得进展,然后不管怎样都发送表
  • <random>不包含在内(也许它在utils.hpp中缺失)

隐藏在这一切之下的是一个问题,即您无法为服务器密钥设置密码回调。这最初在SSL示例中位于load_server_certificate内部。我们不能确定哪些选项适合您的证书,因为我们没有相应的文件。
我确实认为set_verify_mode(ssl::verify_peer)通常与SSL交换中的客户端相关。

修复Demo

忽略丢失的位,下面是修复了上述问题并正常工作的代码:

Live On Coliru

// #include "utils.hpp"
#include <filesystem>
std::filesystem::path path_to_project() { return "."; }

#include <algorithm>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/websocket.hpp>
#include <chrono>
#include <iostream>
#include <memory>
#include <random>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

inline void load_server_certificate(boost::asio::ssl::context& ctx) {
    /*
        The certificate was generated from bash on Ubuntu (OpenSSL 1.1.1f) using:

        openssl dhparam -out dh.pem 2048
        openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 10000 -out cert.pem -subj "/C=US/ST=CA/L=Los Angeles/O=Beast/CN=www.example.com"
    */

    std::string const cert =
        "-----BEGIN CERTIFICATE-----\n"
        "MIIDlTCCAn2gAwIBAgIUOLxr3q7Wd/pto1+2MsW4fdRheCIwDQYJKoZIhvcNAQEL\n"
        "BQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRQwEgYDVQQHDAtMb3MgQW5n\n"
        "ZWxlczEOMAwGA1UECgwFQmVhc3QxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAe\n"
        "Fw0yMTA3MDYwMTQ5MjVaFw00ODExMjEwMTQ5MjVaMFoxCzAJBgNVBAYTAlVTMQsw\n"
        "CQYDVQQIDAJDQTEUMBIGA1UEBwwLTG9zIEFuZ2VsZXMxDjAMBgNVBAoMBUJlYXN0\n"
        "MRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB\n"
        "DwAwggEKAoIBAQCz0GwgnxSBhygxBdhTHGx5LDLIJSuIDJ6nMwZFvAjdhLnB/vOT\n"
        "Lppr5MKxqQHEpYdyDYGD1noBoz4TiIRj5JapChMgx58NLq5QyXkHV/ONT7yi8x05\n"
        "P41c2F9pBEnUwUxIUG1Cb6AN0cZWF/wSMOZ0w3DoBhnl1sdQfQiS25MTK6x4tATm\n"
        "Wm9SJc2lsjWptbyIN6hFXLYPXTwnYzCLvv1EK6Ft7tMPc/FcJpd/wYHgl8shDmY7\n"
        "rV+AiGTxUU35V0AzpJlmvct5aJV/5vSRRLwT9qLZSddE9zy/0rovC5GML6S7BUC4\n"
        "lIzJ8yxzOzSStBPxvdrOobSSNlRZIlE7gnyNAgMBAAGjUzBRMB0GA1UdDgQWBBR+\n"
        "dYtY9zmFSw9GYpEXC1iJKHC0/jAfBgNVHSMEGDAWgBR+dYtY9zmFSw9GYpEXC1iJ\n"
        "KHC0/jAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBzKrsiYywl\n"
        "RKeB2LbddgSf7ahiQMXCZpAjZeJikIoEmx+AmjQk1bam+M7WfpRAMnCKooU+Utp5\n"
        "TwtijjnJydkZHFR6UH6oCWm8RsUVxruao/B0UFRlD8q+ZxGd4fGTdLg/ztmA+9oC\n"
        "EmrcQNdz/KIxJj/fRB3j9GM4lkdaIju47V998Z619E/6pt7GWcAySm1faPB0X4fL\n"
        "FJ6iYR2r/kJLoppPqL0EE49uwyYQ1dKhXS2hk+IIfA9mBn8eAFb/0435A2fXutds\n"
        "qhvwIOmAObCzcoKkz3sChbk4ToUTqbC0TmFAXI5Upz1wnADzjpbJrpegCA3pmvhT\n"
        "7356drqnCGY9\n"
        "-----END CERTIFICATE-----\n";

    std::string const key =
        "-----BEGIN PRIVATE KEY-----\n"
        "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCz0GwgnxSBhygx\n"
        "BdhTHGx5LDLIJSuIDJ6nMwZFvAjdhLnB/vOTLppr5MKxqQHEpYdyDYGD1noBoz4T\n"
        "iIRj5JapChMgx58NLq5QyXkHV/ONT7yi8x05P41c2F9pBEnUwUxIUG1Cb6AN0cZW\n"
        "F/wSMOZ0w3DoBhnl1sdQfQiS25MTK6x4tATmWm9SJc2lsjWptbyIN6hFXLYPXTwn\n"
        "YzCLvv1EK6Ft7tMPc/FcJpd/wYHgl8shDmY7rV+AiGTxUU35V0AzpJlmvct5aJV/\n"
        "5vSRRLwT9qLZSddE9zy/0rovC5GML6S7BUC4lIzJ8yxzOzSStBPxvdrOobSSNlRZ\n"
        "IlE7gnyNAgMBAAECggEAY0RorQmldGx9D7M+XYOPjsWLs1px0cXFwGA20kCgVEp1\n"
        "kleBeHt93JqJsTKwOzN2tswl9/ZrnIPWPUpcbBlB40ggjzQk5k4jBY50Nk2jsxuV\n"
        "9A9qzrP7AoqhAYTQjZe42SMtbkPZhEeOyvCqxBAi6csLhcv4eB4+In0kQo7dfvLs\n"
        "Xu/3WhSsuAWqdD9EGnhD3n+hVTtgiasRe9318/3R9DzP+IokoQGOtXm+1dsfP0mV\n"
        "8XGzQHBpUtJNn0yi6SC4kGEQuKkX33zORlSnZgT5VBLofNgra0THd7x3atOx1lbr\n"
        "V0QizvCdBa6j6FwhOQwW8UwgOCnUbWXl/Xn4OaofMQKBgQDdRXSMyys7qUMe4SYM\n"
        "Mdawj+rjv0Hg98/xORuXKEISh2snJGKEwV7L0vCn468n+sM19z62Axz+lvOUH8Qr\n"
        "hLkBNqJvtIP+b0ljRjem78K4a4qIqUlpejpRLw6a/+44L76pMJXrYg3zdBfwzfwu\n"
        "b9NXdwHzWoNuj4v36teGP6xOUwKBgQDQCT52XX96NseNC6HeK5BgWYYjjxmhksHi\n"
        "stjzPJKySWXZqJpHfXI8qpOd0Sd1FHB+q1s3hand9c+Rxs762OXlqA9Q4i+4qEYZ\n"
        "qhyRkTsl+2BhgzxmoqGd5gsVT7KV8XqtuHWLmetNEi+7+mGSFf2iNFnonKlvT1JX\n"
        "4OQZC7ntnwKBgH/ORFmmaFxXkfteFLnqd5UYK5ZMvGKTALrWP4d5q2BEc7HyJC2F\n"
        "+5lDR9nRezRedS7QlppPBgpPanXeO1LfoHSA+CYJYEwwP3Vl83Mq/Y/EHgp9rXeN\n"
        "L+4AfjEtLo2pljjnZVDGHETIg6OFdunjkXDtvmSvnUbZBwG11bMnSAEdAoGBAKFw\n"
        "qwJb6FNFM3JnNoQctnuuvYPWxwM1yjRMqkOIHCczAlD4oFEeLoqZrNhpuP8Ij4wd\n"
        "GjpqBbpzyVLNP043B6FC3C/edz4Lh+resjDczVPaUZ8aosLbLiREoxE0udfWf2dU\n"
        "oBNnrMwwcs6jrRga7Kr1iVgUSwBQRAxiP2CYUv7tAoGBAKdPdekPNP/rCnHkKIkj\n"
        "o13pr+LJ8t+15vVzZNHwPHUWiYXFhG8Ivx7rqLQSPGcuPhNss3bg1RJiZAUvF6fd\n"
        "e6QS4EZM9dhhlO2FmPQCJMrRVDXaV+9TcJZXCbclQnzzBus9pwZZyw4Anxo0vmir\n"
        "nOMOU6XI4lO9Xge/QDEN4Y2R\n"
        "-----END PRIVATE KEY-----\n";

    std::string const dh =
        "-----BEGIN DH PARAMETERS-----\n"
        "MIIBCAKCAQEArzQc5mpm0Fs8yahDeySj31JZlwEphUdZ9StM2D8+Fo7TMduGtSi+\n"
        "/HRWVwHcTFAgrxVdm+dl474mOUqqaz4MpzIb6+6OVfWHbQJmXPepZKyu4LgUPvY/\n"
        "4q3/iDMjIS0fLOu/bLuObwU5ccZmDgfhmz1GanRlTQOiYRty3FiOATWZBRh6uv4u\n"
        "tff4A9Bm3V9tLx9S6djq31w31Gl7OQhryodW28kc16t9TvO1BzcV3HjRPwpe701X\n"
        "oEEZdnZWANkkpR/m/pfgdmGPU66S2sXMHgsliViQWpDCYeehrvFRHEdR9NV+XJfC\n"
        "QMUk26jPTIVTLfXmmwU0u8vUkpR7LQKkwwIBAg==\n"
        "-----END DH PARAMETERS-----\n";
    
    ctx.set_password_callback(
        [](std::size_t,
            boost::asio::ssl::context_base::password_purpose)
        {
            return "test";
        });

    ctx.set_options(
        boost::asio::ssl::context::default_workarounds |
        boost::asio::ssl::context::no_sslv2 |
        boost::asio::ssl::context::single_dh_use);

    ctx.use_certificate_chain(
        boost::asio::buffer(cert.data(), cert.size()));

    ctx.use_private_key(
        boost::asio::buffer(key.data(), key.size()),
        boost::asio::ssl::context::file_format::pem);

    ctx.use_tmp_dh(
        boost::asio::buffer(dh.data(), dh.size()));
}

// Generate the HTML table content with random values
std::string generate_html_table() {
    std::stringstream ss;
    // Generate a random number of rows and columns
    std::random_device              rd;
    std::mt19937                    gen(rd());
    std::uniform_int_distribution<> dis(1, 10);
    int                             rows = dis(gen);
    int                             cols = dis(gen);
    dis = std::uniform_int_distribution<>(1, 100);

    // Generate a random HTML table with random values
    ss << "<table>\n";
    for (int i = 0; i < rows; i++) {
        ss << "<tr>\n";
        for (int j = 0; j < cols; j++) {
            int value = dis(gen);
            ss << "<td style=\"background-color:rgb(" << value << "," << value << "," << value << ")\">"
               << value << "</td>\n";
        }
        ss << "</tr>\n";
    }
    ss << "</table>\n";

    return ss.str();
}

namespace beast     = boost::beast;         // from <boost/beast.hpp>
namespace http      = beast::http;          // from <boost/beast/http.hpp>
namespace websocket = beast::websocket;     // from <boost/beast/websocket.hpp>
namespace net       = boost::asio;          // from <boost/asio.hpp>
namespace ssl       = boost::asio::ssl;     // from <boost/asio/ssl.hpp>
using tcp           = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>

//------------------------------------------------------------------------------

// Report a failure
void fail(beast::error_code ec, char const* what) { std::cerr << what << ": " << ec.message() << "\n"; }

// Echoes back all received WebSocket messages
class session : public std::enable_shared_from_this<session> {
    websocket::stream<beast::ssl_stream<beast::tcp_stream>> ws_;
    beast::flat_buffer                                      buffer_;

  public:
    // Take ownership of the socket
    session(tcp::socket&& socket, ssl::context& ctx) : ws_(std::move(socket), ctx) {}

    // Get on the correct executor
    void run() {
        // We need to be executing within a strand to perform async operations
        // on the I/O objects in this session. Although not strictly necessary
        // for single-threaded contexts, this example code is written to be
        // thread-safe by default.
        net::dispatch(ws_.get_executor(), beast::bind_front_handler(&session::on_run, shared_from_this()));
    }

    // Start the asynchronous operation
    void on_run() {
        // Set the timeout.
        beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30));

        // Perform the SSL handshake
        ws_.next_layer().async_handshake(
            ssl::stream_base::server, beast::bind_front_handler(&session::on_handshake, shared_from_this()));
    }

    void on_handshake(beast::error_code ec) {
        if (ec)
            return fail(ec, "handshake");

        // Turn off the timeout on the tcp_stream, because
        // the websocket stream has its own timeout system.
        beast::get_lowest_layer(ws_).expires_never();

        // Set suggested timeout settings for the websocket
        ws_.set_option(websocket::stream_base::timeout::suggested(beast::role_type::server));

        // Set a decorator to change the Server of the handshake
        ws_.set_option(websocket::stream_base::decorator([](websocket::response_type& res) {
            res.set(http::field::server, std::string(BOOST_BEAST_VERSION_STRING) + " stackoverflow");
        }));

        // Accept the websocket handshake
        ws_.async_accept(beast::bind_front_handler(&session::on_accept, shared_from_this()));
    }

    void on_accept(beast::error_code ec) {
        if (ec)
            return fail(ec, "accept");

        // Send the colorful HTML table
        send_html_table();
    }

    std::string html;

    void send_html_table() {
        html.clear();
        for (int i = 0; i < 10; i++) {
            html += generate_html_table(); // Generate the HTML table content
        }

        // Create the WebSocket message with the HTML content
        ws_.text(true);
        ws_.async_write(net::buffer(html),
                        [self = shared_from_this()](beast::error_code ec, std::size_t /*bytes_transferred*/) {
                            if (ec)
                                return fail(ec, "write");

                            // Clear the buffer
                            self->buffer_.consume(self->buffer_.size());

                            // Schedule sending the next HTML table after 1 second
                            self->schedule_next_send();
                        });
    }

    void schedule_next_send() {
        // Wait for 1 second before sending the next HTML table
        std::this_thread::sleep_for(std::chrono::seconds(1));

        // Check if the socket is still open
        if (ws_.is_open()) {
            // Send the next HTML table
            send_html_table();
        }
    }
};

//------------------------------------------------------------------------------

// Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener> {
    net::io_context& ioc_;
    ssl::context&    ctx_;
    tcp::acceptor    acceptor_;

  public:
    listener(net::io_context& ioc, tcp::endpoint endpoint, ssl::context& ctx)
        : ioc_(ioc)
        , ctx_(ctx)
        , acceptor_(net::make_strand(ioc)) {
        beast::error_code ec;

        // Open the acceptor
        acceptor_.open(endpoint.protocol(), ec);
        if (ec) {
            fail(ec, "open");
            return;
        }

        // Allow address reuse
        acceptor_.set_option(net::socket_base::reuse_address(true), ec);
        if (ec) {
            fail(ec, "set_option");
            return;
        }

        // Bind to the server address
        acceptor_.bind(endpoint, ec);
        if (ec) {
            fail(ec, "bind");
            return;
        }

        // Start listening for connections
        acceptor_.listen(net::socket_base::max_listen_connections, ec);
        if (ec) {
            fail(ec, "listen");
            return;
        }
    }

    // Start accepting incoming connections
    void run() { do_accept(); }

  private:
    void do_accept() {
        // The new connection gets its own strand
        acceptor_.async_accept(net::make_strand(ioc_),
                               beast::bind_front_handler(&listener::on_accept, shared_from_this()));
    }

    void on_accept(beast::error_code ec, tcp::socket socket) {
        if (ec) {
            fail(ec, "accept");
        } else {
            // Create the session and run it
            std::make_shared<session>(std::move(socket), ctx_)->run();
        }

        // Accept another connection
        do_accept();
    }
};

//------------------------------------------------------------------------------

int main() {
    auto const     address = net::ip::make_address("0.0.0.0");
    uint16_t const port    = 8080;
    auto const     threads = 1;

    // The io_context is required for all I/O
    net::io_context ioc{threads};

    // The SSL context is required, and holds certificates
    ssl::context ctx{ssl::context::tlsv12};

    // Load certificates
    auto path = path_to_project(); // TODO

    load_server_certificate(ctx); // instead for now

    // Create and launch a listening port
    std::make_shared<listener>(ioc, tcp::endpoint{address, port}, ctx)->run();

    // Run the I/O service on the requested number of threads
    std::vector<std::thread> v;
    v.reserve(threads - 1);
    for (auto i = threads - 1; i > 0; --i)
        v.emplace_back([&ioc] { ioc.run(); });
    ioc.run();
}

编译对Coliru来说太重了(事实上用-fsanitize=undefined,address编译在我的电脑上花了8分钟...)。这里有一个本地的demo:

相关问题