listener::on_accept 函数在 Boost 库中的 do_accept 函数结束时不被调用

listener::on_accept function is not called upon end of do_accept function in Boost library

我是使用 BOOST 库的新手。 我目前正在尝试使用 boost 库将数据包发送到 Web-socket 并苦苦挣扎。

我参考了这个 boost 网站 (https://www.boost.org/doc/libs/1_68_0/libs/beast/example/websocket/server/async/websocket_server_async.cpp) 中的模板。

首先,我通过运行在下面的线程函数中调用监听器构造函数。

DWORD WINAPI RunWebServerThread(LPVOID lpParameter)
{
    int threads(1);
    net::io_context ioc{ threads };

    queue<PQN_DATA_PACKET *> *pconnectionQ = (queue<PQN_DATA_PACKET *> *)lpParameter;    
    auto const address = net::ip::make_address(LOCAL_IP);
    // Create and launch a listening port
    std::make_shared<listener>(ioc, tcp::endpoint{ address, SERVER_PORT_NUMBER }, pconnectionQ)->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();    
}

当make_shared函数为运行时,以下函数运行来自boost库。

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

void listener::do_accept()
{
    cout << "DO_ACCEPT LISTENER\n";
    // The new connection gets its own strand
    acceptor_.async_accept(
    net::make_strand(ioc_),
    beast::bind_front_handler(
        &listener::on_accept,
        shared_from_this()));

    cout << "DO_ACCEPT LISTENER END\n";
}

void listener::on_accept(beast::error_code ec, tcp::socket socket)
{
    cout << "ON_ACCEPT LISTENER\n";
    if (ec)
    {
        fail(ec, "accept");
    }
    else
    {
        // Create the session and run it
        std::make_shared<session>(std::move(socket), pconnectionQ, ioc_)->run();
    }

    // Accept another connection
    do_accept();
}

问题是 on_accept 函数似乎没有从 do_accept 函数调用 bind_front_handler 函数。

控制台应显示以下内容。

DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END
ON_ACCEPT LISTENER

但是控制台只显示以下内容。

DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END

如您所见,on_accept() 函数从未被调用,我很难开始调试它,因为 boost 库太复杂了。

谁能建议我在这里做什么?

提前致谢!

您似乎有多个变量名称 ioc

具体来说,RunWebServerThread中有一个是局部变量。然后是您在其他 listener 成员中使用的那个:ioc_.

线程函数中的ioc根据定义不能传到外面,所以我们知道它肯定是独立的实例。

还有很多遗留代码 smell/noise(例如 Win32 API 线程与 std::thread 混合在一起)。我会简化它。

另请注意,使用 std::cout << "\n" 并不能保证将消息刷新到标准输出。添加 std::flush 或使用 std::endl(包括同花顺)。

假设您想与同一服务器的一个或多个侦听器共享 ioc/queues,这是对代码的重组:

Live On Coliru

#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <iostream>
#include <memory>
#include <queue>

namespace net   = boost::asio;
namespace beast = boost::beast;
using net::ip::tcp;
using beast::error_code;

static void fail(error_code ec, std::string_view msg) {
    std::cerr << msg << ": " << ec.message() << std::endl;
    throw std::runtime_error("fail");
}

struct PQN_DATA_PACKET {
};

struct session : std::enable_shared_from_this<session> {
    session(tcp::socket&& socket) : _socket(std::move(socket)) {}

    void run()
    {
        std::cout << "TODO session for " << _socket.remote_endpoint()
                  << std::endl;
    }

  private:
    tcp::socket _socket;
};

using Queue = std::queue<PQN_DATA_PACKET*>;

struct listener : std::enable_shared_from_this<listener> {
    net::io_context& _ioc;
    tcp::acceptor    _acceptor{_ioc};
    Queue&           _connectionQ;

    listener(net::io_context& ioc, tcp::endpoint ep, Queue& connectionQ)
        : _ioc(ioc)
        , _acceptor(ioc, ep)
        , _connectionQ(connectionQ)
    {
    }

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

    void do_accept()
    {
        std::cout << "DO_ACCEPT LISTENER" << std::endl;
        // The new connection gets its own strand
        _acceptor.async_accept( //
            net::make_strand(_ioc),
            beast::bind_front_handler(&listener::on_accept,
                                      shared_from_this()));

        std::cout << "DO_ACCEPT LISTENER END" << std::endl;
    }

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

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

struct server {
    net::io_context           _ioc;
    Queue                     _connectionQ;

    void run(tcp::endpoint ep, unsigned nthreads = 1) {
        std::make_shared<listener>(_ioc, ep, _connectionQ) //
            ->run();

        // Run the I/O service on the requested number of threads
        std::vector<std::thread> v(nthreads);

        for (auto& th : v)
            th = std::thread([this] { _ioc.run(); });

        for (auto& th : v)
            if (th.joinable())
                th.join();
    }
};

int main() {
    static uint16_t constexpr SERVER_PORT_NUMBER = 9797;

    server srv;
    srv.run({{}, SERVER_PORT_NUMBER}, 1);
}

打印

DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END
ON_ACCEPT LISTENER
TODO session for 127.0.0.1:45930
DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END
ON_ACCEPT LISTENER
TODO session for 127.0.0.1:45932
DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END
ON_ACCEPT LISTENER
TODO session for 127.0.0.1:45934
DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END
ON_ACCEPT LISTENER
TODO session for 127.0.0.1:45936
DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END
ON_ACCEPT LISTENER
TODO session for 127.0.0.1:45938
DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END
ON_ACCEPT LISTENER
TODO session for 127.0.0.1:45940
DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END
ON_ACCEPT LISTENER
TODO session for 127.0.0.1:45942
DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END
ON_ACCEPT LISTENER
TODO session for 127.0.0.1:45944
DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END
ON_ACCEPT LISTENER
TODO session for 127.0.0.1:45946
DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END
ON_ACCEPT LISTENER
TODO session for 127.0.0.1:45948
DO_ACCEPT LISTENER
DO_ACCEPT LISTENER END

更多

我建议使用 boost::asio::thread_pool 而不是自己滚动(记住 Should the exception thrown by boost::asio::io_service::run() be caught? 例如)

请记住,在执行程序存在的情况下传递引用 ioc 是一种代码味道(您可能应该使用 _socket.get_executor() 或更本地地创建链?