在Linux上使用async_receive_from后无法接收UDP数据包,但在Windows上可以工作

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

我现在正在编写一个C++程序,它使用UDP与硬件设备发送和接收消息。该程序在 Windows 上运行良好,但当我将相同的代码放在 Linux(Ubuntu 22.04 LTS)上时,它运行得很糟糕。有时它可以收到数据包,但大多数情况下它什么也没收到。

这是我的代码的一部分:

JULidar::JULidar(const std::string& local_ip, const std::string& local_port,
    const std::string& device_ip, const std::string& device_port)
    : io_context(),
    socket(io_context),
    local_endpoint(boost::asio::ip::address::from_string(local_ip), std::stoi(local_port)),
    device_endpoint(boost::asio::ip::address::from_string(device_ip), std::stoi(device_port)),
    receive_thr(&JULidar::receive_thread, this),
    process_thr(&JULidar::process_thread, this),
    output_flag(false),
    param_pkg_operated_completed(false),
    frame_update_completed(false)
{
    try
    {
        std::cout << "binding local ip ..." << std::endl;
        socket.open(boost::asio::ip::udp::v4());
        socket.bind(local_endpoint);
        asyncRecvFrom();
    }
    catch (boost::system::system_error)
    {
        std::cout << "local network config error, please modify the ip & port above." << std::endl;
    }
}

JULidar::~JULidar()
{
    socket.close();
    io_context.stop();
    receive_thr.interrupt();
    receive_thr.join();

    process_thr.interrupt();
    process_thr.join();
}

void JULidar::asyncRecvFrom()
{
    boost::system::error_code error;
    socket.async_receive_from(
        boost::asio::buffer(udpBuffer), 
        device_endpoint, 
        boost::bind(
            &JULidar::recvHandler, 
            this, 
            boost::asio::placeholders::error, 
            boost::asio::placeholders::bytes_transferred));
}

void JULidar::recvHandler(const boost::system::error_code& error, size_t bytes_received)
{
    if (bytes_received != 0)
    {
        // ...
    }
    asyncRecvFrom();
}

void JULidar::receive_thread()
{
    while (1)
    {
        try
        {
            io_context.run();
            boost::this_thread::interruption_point();
        }
        catch (...)
        {
            break;
        }
    }
    std::cout << "recv_thr ending..." << std::endl;
}

我创建了一个线程来运行 io_context 并不断从设备端点接收消息。每次数据包到达时,函数recvHandler()都会执行一些操作。它在 Windows 上按预期工作。为什么它不能在 Linux 上运行?

如果有人可以提供帮助,我真的很感激!!!

如果程序能够像在 Windows 上一样运行,那就太好了。

c++ linux sockets udp boost-asio
1个回答
0
投票
  1. 您正在以非典型(“错误”)方式使用执行上下文。例如在这个循环中

    while (1) {
        try {
            io_context.run();
            boost::this_thread::interruption_point();
        } catch (...) {
            break;
        }
    }
    

    整个

    interruption_point()
    仅当
    io_context.run()
    预计将耗尽工作时才有用。但如果这是真的,那么代码是错误的,因为
    io_context.restart()
    (或以前的
    reset()
    )永远不会被调用,而这是必需的。

  2. 在打开套接字/发布第一个异步工作之前创建线程也存在问题。这意味着

    io_context
    可能会立即失去工作,即在工作开始之前。

  3. Linux 和 Windows 之间的行为差异可以通过时间差异来解释。

    请记住,UDP 无法保证传送,因此如果您的计算机“繁忙”,一些数据包丢失是可以预料的。


我会使用工作防护来重写逻辑,以避免上下文耗尽工作:

住在Coliru

#include <boost/asio.hpp> #include <boost/asio/serial_port.hpp> #include <boost/thread.hpp> #include <iomanip> #include <iostream> namespace asio = boost::asio; using asio::ip::udp; using boost::system::error_code; struct JULidar { JULidar(std::string const& local_ip, std::string const& local_port, // std::string const& device_ip, std::string const& device_port); ~JULidar(); private: void asyncRecvFrom(); void recvHandler(error_code error, size_t bytes_received); void receive_thread(); void process_thread() { // TODO } using Executor = asio::io_context::executor_type; using Work = asio::executor_work_guard<Executor>; asio::io_context io_context; Work work{io_context.get_executor()}; udp::socket socket{io_context}; udp::endpoint local_endpoint, device_endpoint; std::array<char, 65000> udpBuffer; std::thread receive_thr, process_thr; std::atomic_bool output_flag{false}; std::atomic_bool param_pkg_operated_completed{false}; std::atomic_bool frame_update_completed{false}; }; JULidar::JULidar(std::string const& local_ip, std::string const& local_port, std::string const& device_ip, std::string const& device_port) try : local_endpoint(asio::ip::address::from_string(local_ip), static_cast<uint16_t>(std::stoi(local_port))) , device_endpoint(asio::ip::address::from_string(device_ip), static_cast<uint16_t>(std::stoi(device_port))) , receive_thr(&JULidar::receive_thread, this) , process_thr(&JULidar::process_thread, this) // { std::cout << "binding local ip ..." << std::endl; socket.open(local_endpoint.protocol()); socket.bind(local_endpoint); asyncRecvFrom(); } catch (boost::system::system_error const&) { std::cout << "local network config error, please modify the ip & port above." << std::endl; } JULidar::~JULidar() { post(socket.get_executor(), [this] { socket.cancel(); }); work.reset(); // allow the context to run out of work if (receive_thr.joinable()) receive_thr.join(); if (process_thr.joinable()) process_thr.join(); } void JULidar::asyncRecvFrom() { using namespace std::placeholders; socket.async_receive_from(asio::buffer(udpBuffer), device_endpoint, std::bind(&JULidar::recvHandler, this, _1, _2)); } void JULidar::recvHandler(error_code error, size_t bytes_received) { std::cerr << "recvHandler(" << error.message() << ", " << bytes_received << ")" << std::endl; if (!error.failed()) { if (bytes_received != 0) { // ... } asyncRecvFrom(); } } void JULidar::receive_thread() { for (;;) { try { io_context.run(); break; // exited normally } catch (std::exception const& e) { std::cerr << "[receive_thread] " << e.what() << std::endl; } catch (...) { std::cerr << "[receive_thread] unknown exception" << std::endl; } } std::cout << "receive_thread ending..." << std::endl; } int main() { { JULidar lidar("127.0.0.1", "8989", "127.0.0.1", "8990"); using namespace std::literals; std::this_thread::sleep_for(30s); } // destructor will shutdown io_context }
本地演示

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