使用Boost.Asio创建UDP接收线程时遇无效参数错误求助
问题:Boost ASIO UDP接收线程报错(错误码10022)
问题背景
我在C++中使用Boost ASIO库创建线程监听UDP响应,类中接收线程的代码如下:
tello::tello(boost::asio::io_service& io_service, boost::asio::ip::udp::socket& socket, const boost::asio::ip::udp::endpoint& remote_endpoint) : ip(remote_endpoint.address().to_string()), port(remote_endpoint.port()), io_service(io_service), socket(socket), remote_endpoint(remote_endpoint) { } void tello::ReceiveThread() { while (isListening_) { try { char receivedData[1024]; boost::system::error_code error; size_t len = socket.receive_from(boost::asio::buffer(receivedData), remote_endpoint, 0, error); if (error && error != boost::asio::error::message_size) { throw boost::system::system_error(error); } receivedData[len] = '\0'; spdlog::info("Received UDP data: {}", receivedData); } catch (const std::exception& e) { std::cerr << "Error receiving UDP data: " << e.what() << std::endl; } } } void tello::StartListening() { std::thread udpThread(&tello::ReceiveThread, this); udpThread.detach(); // Detach the thread so it runs independently } void tello::StopListening() { isListening_ = false; }
main.cpp中的启动代码:
boost::asio::io_service io_service; boost::asio::ip::udp::socket socket(io_service); boost::asio::ip::udp::endpoint remote_endpoint(boost::asio::ip::address::from_string("192.168.0.1"), 12345); // Replace with the actual IP address and port socket.open(boost::asio::ip::udp::v4()); tello drone(io_service, socket, remote_endpoint); drone.StartListening();
代码编译无错误,但运行时控制台报错:
Error receiving UDP data: An invalid argument was supplied [system:10022 at C:\Users\(username)\boost_1_82_0\boost\asio\detail\win_iocp_socket_service.hpp:418:5 in function 'receive_from']
环境:Windows 64位系统、C++14、Boost 1.82
解决方案
1. 绑定UDP Socket到本地端口
UDP Socket调用open()后必须绑定到本地端口才能接收数据,否则操作系统无法确定监听目标。在main.cpp的socket.open()后添加绑定代码:
// 绑定到任意可用本地端口,也可指定固定端口(如8889) boost::asio::ip::udp::endpoint local_endpoint(boost::asio::ip::udp::v4(), 0); socket.bind(local_endpoint);
2. 修正receive_from的端点参数
receive_from的第二个参数是输出参数,用于存储发送方的端点信息,不能直接传入构造时的remote_endpoint(该对象为const引用,且receive_from会修改它)。修改ReceiveThread代码:
void tello::ReceiveThread() { while (isListening_) { try { char receivedData[1024]; boost::system::error_code error; // 创建临时端点对象,用于接收发送方的地址和端口 boost::asio::ip::udp::endpoint sender_endpoint; size_t len = socket.receive_from(boost::asio::buffer(receivedData), sender_endpoint, 0, error); if (error && error != boost::asio::error::message_size) { throw boost::system::system_error(error); } receivedData[len] = '\0'; spdlog::info("Received UDP data from {}:{}: {}", sender_endpoint.address().to_string(), sender_endpoint.port(), receivedData); } catch (const std::exception& e) { std::cerr << "Error receiving UDP data: " << e.what() << std::endl; } } }
3. 保证isListening_的线程安全性
isListening_被多个线程读写,必须使用原子类型避免竞态条件。在tello类中修改声明:
#include <atomic> class tello { // ... 其他成员 ... std::atomic<bool> isListening_{false}; // ... 其他成员 ... };
同时修改启停函数,确保状态切换的原子性:
void tello::StartListening() { isListening_ = true; std::thread udpThread(&tello::ReceiveThread, this); udpThread.detach(); } void tello::StopListening() { isListening_ = false; // 关闭socket唤醒阻塞的receive_from调用,避免线程无限等待 socket.close(); }
4. 阻止主线程提前退出(可选)
如果主线程直接退出,会导致子线程被强制终止。在main函数末尾添加阻塞逻辑:
// 等待用户输入后退出 std::string input; std::cout << "Press enter to exit..." << std::endl; std::getline(std::cin, input); drone.StopListening();
内容的提问来源于stack exchange,提问作者Ethan James
相关产品推荐
相关产品推荐

