我现在正在编写一个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 上一样运行,那就太好了。
您正在以非典型(“错误”)方式使用执行上下文。例如在这个循环中
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
}
本地演示