Linux boost::asio::spawn 中无法完成连接

问题描述 投票:0回答:1

如果服务器启动后无法连接,为什么? boost_1_73_0、Linux操作系统

#include "boost/asio.hpp"
#include "boost/asio/spawn.hpp"

int main() {
    boost::asio::io_context ctx;
    auto ep        = boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address("127.0.0.1"), 1560);
    bool isConnect = false;
    bool isRun     = false;
    boost::asio::ip::tcp::socket socket(ctx);
    while (true) {
        if (!isConnect && !isRun) {
            boost::asio::spawn([&](boost::asio::yield_context y) {
                isRun = true;
                boost::system::error_code ec;
                socket.async_connect(ep, y[ec]);
                if (ec) {
                    isRun = false;
                    socket.close();
                    printf("close\n");
                } else {
                    isConnect = true;
                    printf("connect\n");
                }
            });
        }
        ctx.poll_one();
        Sleep(1);
    }
}

它在 Windows 上运行良好。 我的帖子看起来主要是代码,所以我添加了这句话。 非常感谢你

linux boost-asio asio
1个回答
0
投票

要事第一

Sleep
是 WinAPI 调用。它预计毫秒。

我怀疑您可能已通过...将

Sleep
小写为
sleep
将其移植到 Linux。然而
sleep
需要几秒钟。所以每次投票你可能要等待 1000 秒。

通过不使用任何特定于平台的调用而是使用已经在我的机器上“运行”的标准库函数来解决这个问题:

住在科里鲁

#include "boost/asio.hpp"
#include "boost/asio/spawn.hpp"
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
using namespace std::chrono_literals;
using boost::system::error_code;
using std::this_thread::sleep_for;

int main() {
    asio::io_context ctx;
    auto ep        = tcp::endpoint({}, 1560);
    bool             isConnect = false, isRun = false;
    tcp::socket      socket(ctx);
    while (true) {
        if (!isConnect && !isRun) {
            asio::spawn([&](asio::yield_context y) {
                isRun = true;
                error_code ec;
                socket.async_connect(ep, y[ec]);
                if (ec) {
                    isRun = false;
                    socket.close();
                    std::cout << ec.message() << ": close" << std::endl;
                } else {
                    isConnect = true;
                    std::cout << ec.message() << ": connect" << std::endl;
                }
            });
        }
        ctx.poll_one();
        sleep_for(1s);
    }
}

下一步:选择您的环境!

spawn
没有上下文。这意味着它将默认为生成函数的关联执行器的一个链。没有,所以默认为
system_executor
。您可以通过从 coro 内部打印有效执行器来看到这一点:

std::cerr << boost::core::demangle(y.get_executor().target_type().name()) << std::endl;

将打印

boost::asio::strand<boost::asio::basic_system_executor<
    boost::asio::execution::detail::blocking::possibly_t<0>,
    boost::asio::execution::detail::relationship::fork_t<0>, std::allocator<void>>>

使用

spawn(ctx, ...)
你会看到:

boost::asio::strand<boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul>>

这意味着

ctx.poll()
永远不会运行与您的coro或socket相关的任何内容,因为它们位于不同的执行上下文中。

或者,您可以通过使用

bind_executor(ctx.get_executor, [&](...) {...})

将执行器绑定到 coro 函数来修复它

为什么它看起来有效?

它似乎可以在上面工作,因为至少在Linux上

system_executor
默认是一个在后台运行线程的线程池。

这很可怕,因为它使您的程序在您不知情的情况下成为多线程:所有共享对象上都有潜在的数据竞争等待发生,以及布尔标志的负载可以轻松优化的问题一种永远不会检测到变化的方式。

目标是什么

但是,目前还不清楚轮询循环实际上完成了什么。您可以通过将生成从循环中取出并用更强大的循环替换来简化很多

run()
/
run_for()
:

int main() {
    asio::io_context ctx;
    tcp::socket      socket(ctx);

    tcp::endpoint ep{{}, 1560};
    asio::spawn(ctx, [&](asio::yield_context y) {
        std::cerr << boost::core::demangle(y.get_executor().target_type().name()) << std::endl;

        error_code ec;
        socket.async_connect(ep, y[ec]);
        if (ec) {
            socket.close();
            std::cout << ec.message() << ": close" << std::endl;
        } else {
            std::cout << ec.message() << ": connect" << std::endl;
        }
    });

    ctx.run_for(10s); // max 10s
}

这可以正常工作,并且无需多线程。

© www.soinside.com 2019 - 2024. All rights reserved.