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

clj7thdc  于 2023-10-16  发布在  Linux
关注(0)|答案(1)|浏览(229)

我现在正在写一个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,并不断接收来自设备端点的消息。每次数据包到达时,函数recvc()都会做一些事情。它在Windows上的工作正如预期的那样。为什么它不能在Linux上运行?
真的很感谢任何人可以帮助!!!
这将是伟大的,如果该计划可以工作,因为它如何在Windows上工作。

3z6pesqy

3z6pesqy1#

1.您正在以一种非典型(“错误”)的方式使用执行上下文。例如,在此循环中

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

整个interruption_point()只有在io_context.run()预期用完工作时才有用。如果这是真的,那么代码就错了,因为io_context.restart()(或以前的reset())从未被调用,而这是必需的。
1.还有一个问题是,在套接字被打开/第一次发布之前就创建了线程。这意味着io_context可能已经立即用完了工作,即。在工作开始之前。

  1. Linux和Windows之间的行为差异可能是由于时间差异。
    请记住,UDP不能保证传输,所以如果您的机器“忙碌”,一些数据包丢失是可以预料的。
    我会使用一个工作保护来重写逻辑,以 * 避免 * 上下文工作不足:

Live On 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
}

本地演示

相关问题