如何在 boost::asio udp::socket 接收时设置超时?

发布于 2025-01-11 15:51:49 字数 611 浏览 0 评论 0原文

我创建了一个单线程应用程序,它通过 UDP 与另一个线程进行交换。当第二个断开连接时,我的 socket::receive_from 阻塞,我不知道如何解决这个问题,而不将整个程序更改为多线程或异步交互。

我认为接下来可能是一个解决方案:

std::chrono::milliseconds timeout{4};
boost::system::error_code err;
data_t buffer(kPackageMaxSize);
std::size_t size = 0;

const auto status = std::async(std::launch::async,
    [&]{
        size = socket_.receive_from(boost::asio::buffer(buffer), dst_, 0, err);
    }
).wait_for(timeout);

switch (status)
{
    case std::future_status::timeout: /*...*/ break;
}

但是我遇到了一个新问题:Qt Creator(GDB 11.1)(我还没有能力尝试一些东西)在调试时开始崩溃。如果没有它运行,该解决方案也并不总是有效。

I create an one-thread application which exchanges with another one via UDP. When the second is disconnecting, my socket::receive_from blocks and I don't know how to solve this problem not changing the entire program into multi-threads or async interactions.

I thought that next may be a solution:

std::chrono::milliseconds timeout{4};
boost::system::error_code err;
data_t buffer(kPackageMaxSize);
std::size_t size = 0;

const auto status = std::async(std::launch::async,
    [&]{
        size = socket_.receive_from(boost::asio::buffer(buffer), dst_, 0, err);
    }
).wait_for(timeout);

switch (status)
{
    case std::future_status::timeout: /*...*/ break;
}

But I achieved a new problem: Qt Creator (GDB 11.1) (I don't have ability to try something yet) began to fall when I am debugging. If it runs without, the solution also not always works.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

秋叶绚丽 2025-01-18 15:51:49

PS。至于“调试时不起作用”,调试(特别是断点)显然会改变时序。另外,请记住网络操作具有不同的延迟,并且 UDP 不是有保证的协议:消息可能无法传递。


Asio 代表“异步 IO”。正如您可能怀疑的那样,这意味着异步 IO 是一个内置功能,它是该库的全部目的。请参阅overview/core/async.html: 没有线程的并发

没有必要使std::async变得复杂。对于您的情况,我建议将 async_receive_fromuse_future 一起使用,因为它最接近您选择的模型:

Live On Coliru

#include <boost/asio.hpp>
#include <iostream>
#include <iomanip>
namespace net = boost::asio;
using net::ip::udp;

using namespace std::chrono_literals;
constexpr auto kPackageMaxSize = 65520;
using data_t = std::vector<char>;

int main() {
    net::thread_pool ioc;

    udp::socket socket_(ioc, udp::v4());
    socket_.bind({{}, 8989});

    udp::endpoint ep;
    data_t        buffer(kPackageMaxSize);
    auto          fut =
        socket_.async_receive_from(net::buffer(buffer), ep, net::use_future);

    switch (fut.wait_for(4ms)) {
        case std::future_status::ready: {
            buffer.resize(fut.get()); // never blocks here
            std::cout << "Received " << buffer.size() << " bytes: "
                      << std::quoted(
                             std::string_view(buffer.data(), buffer.size()))
                      << "\n";
            break;
        }
        case std::future_status::timeout:
        case std::future_status::deferred: {
            std::cout << "Timeout\n";
            socket_.cancel(); // stop the IO operation
            // fut.get() would throw system_error(net::error::operation_aborted)
            break;
        }
    }

    ioc.join();
}

Coliru 输出:

Received 12 bytes: "Hello World
"

本地演示超时和成功路径:

在此处输入图像描述

PS. As for "it doesn't work when debugging", debugging (specifically breakpoints) obviously changes timing. Also, keep in mind network operations have varying latency and UDP isn't a guaranteed protocol: messages may not be delivered.


Asio stands for "Asynchronous IO". As you might suspect, this means that asynchronous IO is a built-in feature, it's the entire purpose of the library. See overview/core/async.html: Concurrency Without Threads

It's not necessary to complicate with std::async. In your case I'd suggest using async_receive_from with use_future, as it is closest to the model you opted for:

Live On Coliru

#include <boost/asio.hpp>
#include <iostream>
#include <iomanip>
namespace net = boost::asio;
using net::ip::udp;

using namespace std::chrono_literals;
constexpr auto kPackageMaxSize = 65520;
using data_t = std::vector<char>;

int main() {
    net::thread_pool ioc;

    udp::socket socket_(ioc, udp::v4());
    socket_.bind({{}, 8989});

    udp::endpoint ep;
    data_t        buffer(kPackageMaxSize);
    auto          fut =
        socket_.async_receive_from(net::buffer(buffer), ep, net::use_future);

    switch (fut.wait_for(4ms)) {
        case std::future_status::ready: {
            buffer.resize(fut.get()); // never blocks here
            std::cout << "Received " << buffer.size() << " bytes: "
                      << std::quoted(
                             std::string_view(buffer.data(), buffer.size()))
                      << "\n";
            break;
        }
        case std::future_status::timeout:
        case std::future_status::deferred: {
            std::cout << "Timeout\n";
            socket_.cancel(); // stop the IO operation
            // fut.get() would throw system_error(net::error::operation_aborted)
            break;
        }
    }

    ioc.join();
}

The Coliru output:

Received 12 bytes: "Hello World
"

Locally demonstrating both timeout and successful path:

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文