提问人:Jiahao Zhu 提问时间:9/20/2023 最后编辑:Jiahao Zhu 更新时间:9/20/2023 访问量:33
在 Linux 上使用 async_receive_from 后无法接收 UDP 数据包,但可以在 Windows 上运行
Cannot receive UDP packets after using async_receive_from on Linux, but can work on Windows
问:
我现在正在编写一个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 上一样工作,那就太好了。
答:
0赞
sehe
9/20/2023
#1
您正在以一种非典型(“错误”)的方式使用执行上下文。例如,在这个循环中
while (1) { try { io_context.run(); boost::this_thread::interruption_point(); } catch (...) { break; } }
只有当预计会用完工作时,整个人才有用。但是,如果这是真的,则代码是错误的,因为(或,以前)从未被调用过,这将是必需的。
interruption_point()
io_context.run()
io_context.restart()
reset()
在打开套接字/发布第一个异步工作之前创建的线程也存在问题。这意味着可能立即用完工作,即在工作开始之前。
io_context
Linux 和 Windows 之间的行为差异可以用时序差异来解释。
请记住,UDP 不保证交付,因此如果您的机器“繁忙”,一些数据包丢失是意料之中的。
我会使用工作守卫重写逻辑,以避免上下文耗尽工作:
#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
}
本地演示
评论
0赞
Jiahao Zhu
9/21/2023
非常感谢!我已经在这个问题上工作了好几天,现在它起作用了。也许我需要回顾一下套接字编程。
评论