Boost Async Websocket服务器问题

时间:2019-05-14 15:17:33

标签: boost-asio boost-beast beast-websockets

我通过boost.beast写了一个异步websocket。但是当我尝试运行它时,无法连接它。

服务器代码如下。当我尝试连接Websocket服务器时,我的镶边显示连接状态。 当我通过VS2017进行调试时,它永远不会在run()的lambda表达式中运行。


iListener::iListener( boost::asio::io_context& ioc,boost::asio::ip::tcp::endpoint endpoint)  : acceptor_(ioc), socket_(ioc) {

    boost::system::error_code ec;

    std::cout<<"iListener"<<std::endl;
    // Open the acceptor
    acceptor_.open(endpoint.protocol(), ec);
    if (ec) {
        // fail(ec, "open");
        return;
    }

    // Allow address reuse
    acceptor_.set_option(boost::asio::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(
            boost::asio::socket_base::max_listen_connections, ec);
    if (ec) {

        std::cout << ec.message() << "   listen" << std::endl;
        // fail(ec, "listen");
        return;
    }
}

iListener::~iListener() {

}

void iListener::run() {
    if (!acceptor_.is_open()) {
        return;
    }
    std::cout<<"iListener run"<<std::endl;
    while (true) {
        acceptor_.async_accept(socket_, [&](boost::system::error_code ec1) {
            std::cout << "now run listener" << std::endl;
            if (ec1) {
                std::cout<<ec1.message()<<"   accept"<<std::endl;
                // fail(ec, "accept");
            } else {
                // Create the session and run it
                std::make_shared<NormalSession>(std::move(socket_))->run();
            }
        });
    }


}

void iListener::initListener(const std::string &addressStr, unsigned short port, int threads){
    auto const address = boost::asio::ip::make_address(addressStr);
    boost::asio::io_context ioc{threads};
    std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})->run();
    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();
}

当我尝试在Chrome控制台上进行连接时。 连接时间很长,然后显示失败。


所以我改回去,以升压为例。


void iListener::run() {
    if (!acceptor_.is_open()) {
        return;
    }
   // std::cout<<"iListener run"<<std::endl;
   // while (true) {
   //     acceptor_.async_accept(socket_, [&](boost::system::error_code ec1) {
            //std::cout << "now run listener" << std::endl;
   //         if (ec1) {
   //             std::cout<<ec1.message()<<"   accept"<<std::endl;
   //             // fail(ec, "accept");
   //         } else {
   //             // Create the session and run it
   //             std::make_shared<NormalSession>(std::move(socket_))->run();
   //         }
   //     });
   // }
    do_accept();

}

void iListener::do_accept() {
    acceptor_.async_accept(
        socket_,
        std::bind(
            &iListener::on_accept,
            shared_from_this(),
            std::placeholders::_1));
}

void    iListener::on_accept(boost::system::error_code ec) {
    if (ec)
    {
        std::cout << ec.message() << "   on_accept" << std::endl;
    }
    else
    {
        // Create the session and run it
        std::make_shared<NormalSession>(std::move(socket_))->run();
    }

    // Accept another connection
    do_accept();
}

我有两个问题:

1。为什么我使用lambda,它将是SOF,但示例不会。 2.当我使用while()时,它不起作用,为什么? lambda表达式和std :: bind()之间有什么区别?


还有另一个问题,下面的两个代码块有什么区别?

void iListener::do_accept() {
    acceptor_.async_accept(
        socket_,
        [&](boost::system::error_code ec1) mutable {
        on_accept(ec1);
    }
}


void iListener::do_accept() {
    acceptor_.async_accept(
        socket_,
        std::bind(
            &iListener::on_accept,
            shared_from_this(),
            std::placeholders::_1));
}

当我使用前一个时,它返回error_code 995。

1 个答案:

答案 0 :(得分:1)

编辑

bindlambda有什么区别?在前者中,您可以延长iListener实例的寿命,而在后者中则不需要。

我们需要从这一行开始:

std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})->run();
// [a]

如果您不延长iListenerrun的生存期,则在[a]行iListener中的实例将被销毁。

  • std :: bind

作为绑定的参数之一,您正在传递shared_from_this,它会从shared_ptr指针创建this,因此bind返回的函子对象将智能指针保持为{{1 }}实例可以延长其寿命。

  • lambda
iListener

生存期未延长,您调用iListener传递lambda,而不会增加当前对象的引用计数器,即,调用了async_accept的对象。因此do_accept立即返回,async_accept结束,最后do_accept也结束,并且run创建的对象被删除。

您需要通过将std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})的值传递到lambda中来更新引用计数器:

shared_ptr

void iListener::do_accept() { auto sp = shared_from_this(); acceptor_.async_accept( socket_, [&,sp](boost::system::error_code ec1) mutable { on_accept(ec1); } } 调用async_accept发起的任务的处理程序(在您的情况下为lambda主体),您看不到此执行,因为您的代码挂在此行上:

io_context::run

这将创建std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})->run(); 实例并调用iListener,该实例包含无限循环且永远不会结束:

run

因此您无法到达以while (true) { // INFINITE LOOP acceptor_.async_accept(socket_, [&](boost::system::error_code ec1) { std::cout << "now run listener" << std::endl; if (ec1) { std::cout<<ec1.message()<<" accept"<<std::endl; // fail(ec, "accept"); } else { // Create the session and run it std::make_shared<NormalSession>(std::move(socket_))->run(); } }); } 开头的可调用处理程序的行。

修复:启动io_context::run之前,您可以启动另一个执行io_context::run的线程。

访问Boost Asio examples,以了解如何使用iListener::run。常见的方法是从其处理程序中调用async_accept,但是如果要执行此操作,则async_accept应该从iListener派生,以延长传递到处理程序中的寿命。


另一个问题是enable_shared_from_this数据成员。我假设您想在每个会话中容纳一个套接字,但是现在您的代码无法正确处理它。您只有socket_的一个实例,如果建立了新的连接,该实例将被移到socket_中。因此,当第二次调用NormalSession时,您正在传递INVALID套接字。它行不通。这会导致不确定的行为。

执行以下行后

async_accept

您可能会忘记std::make_shared<NormalSession>(std::move(socket_))->run();

socket_已超载,您可以将带有handler的版本与新接受的套接字一起使用。

但是,如果您希望保持当前版本为async_accept,则需要确保每次调用socket时都使用唯一的套接字实例。

相关问题