基于ASIO的UDP消息收发:发送成功但循环接收失败
UDP异步收发故障排查(Asio库)
我用Asio库实现了UDP消息收发功能,发送的Header+Body消息仅用于测试,复用了为TCP编写的Message结构。目前客户端能成功发送消息(输出"Sent"日志确认),但服务器循环调用UDP::Recieve函数时无法接收消息。服务器Socket绑定6001端口,客户端Socket绑定6000端口,客户端向6001端口发送消息后,服务器无任何接收日志输出。相关代码如下:
void UDP::Recieve(Func inRecieve) { mUDPSocket.async_receive( asio::buffer(&mTemporaryMessageBuffer.mHeader, sizeof(MessageHeader)), [&](std::error_code ec, size_t inSize) { if (not ec) { std::cout << "Received:" << inSize << "\n"; auto &msg = mTemporaryMessageBuffer; msg.mBody.resize(msg.size()); mUDPSocket.async_receive( asio::buffer(mTemporaryMessageBuffer.mBody.data(), msg.mBody.size()), [&](std::error_code ec, size_t inSize) { if (not ec) { std::cout << "Received:" << inSize << "\n"; inRecieve(mTemporaryMessageBuffer); } else { std::cout << "ERROR:" << ec.message() << "\n"; } std::flush(std::cout); }); } else { std::cout << "ERROR:" << ec.message() << "\n"; } std::flush(std::cout); }); } void UDP::Send(const Message &inMessage, std::string inDestination, int inPort) { asio::ip::udp::endpoint Remote(asio::ip::address::from_string(s(inDestination)), inPort); mUDPSocket.async_send_to(asio::buffer(&inMessage.mHeader, sizeof(MessageHeader)), Remote, [&](std::error_code ec, size_t inSize) { if (not ec) { std::cout << "Sent" << inSize << "\n"; } else { std::cout << "ERROR:" << ec.message() << "\n"; } std::flush(std::cout); }); mUDPSocket.async_send_to(asio::buffer(inMessage.mBody.data(), inMessage.mBody.size()), Remote, [&](std::error_code ec, size_t inSize) { if (not ec) { std::cout << "Sent" << inSize << "\n"; } else { std::cout << "ERROR:" << ec.message() << "\n"; } std::flush(std::cout); }); } } // namespace Jkr::Network
核心问题与修复方案
1. UDP接收API误用
UDP是无连接协议,async_receive仅适用于已通过connect绑定固定端点的UDP套接字。你的服务器未提前与客户端建立连接,必须使用async_receive_from来接收任意发送方的消息,同时获取发送方端点信息。
2. 分段收发的风险
UDP基于报文传输,你将Header和Body拆分为两次async_send_to发送,会生成两个独立UDP报文;接收时也分两次读取,可能出现报文顺序错乱、部分丢失的情况,导致消息结构破坏。正确做法是将Header+Body合并为一个缓冲区一次性发送。
3. Lambda引用捕获的生命周期问题
代码中用[&]捕获局部变量引用,Recieve函数返回后,inRecieve等变量会被销毁,lambda内的引用会变成悬空引用,触发未定义行为。需改用值捕获或延长变量生命周期。
修复后的代码示例
接收函数修正
void UDP::Recieve(Func inRecieve) { // 用shared_ptr保存发送方端点,避免生命周期问题 auto remote_endpoint = std::make_shared<asio::ip::udp::endpoint>(); mUDPSocket.async_receive_from( asio::buffer(&mTemporaryMessageBuffer.mHeader, sizeof(MessageHeader)), *remote_endpoint, [this, inRecieve, remote_endpoint](std::error_code ec, size_t inSize) { if (!ec) { std::cout << "Received header:" << inSize << "\n"; auto &msg = mTemporaryMessageBuffer; // 假设Header的size字段表示Body长度 msg.mBody.resize(msg.mHeader.size); mUDPSocket.async_receive_from( asio::buffer(msg.mBody.data(), msg.mBody.size()), *remote_endpoint, [this, inRecieve, remote_endpoint, &msg](std::error_code ec, size_t inSize) { if (!ec) { std::cout << "Received body:" << inSize << "\n"; inRecieve(msg); // 递归调用维持接收循环 Recieve(inRecieve); } else { std::cout << "ERROR receiving body:" << ec.message() << "\n"; } std::flush(std::cout); }); } else { std::cout << "ERROR receiving header:" << ec.message() << "\n"; } std::flush(std::cout); }); }
发送函数修正
void UDP::Send(const Message &inMessage, std::string inDestination, int inPort) { asio::ip::udp::endpoint Remote(asio::ip::address::from_string(inDestination), inPort); // 合并Header和Body为完整缓冲区 std::vector<char> full_buffer; full_buffer.reserve(sizeof(MessageHeader) + inMessage.mBody.size()); // 复制Header std::copy_n(reinterpret_cast<const char*>(&inMessage.mHeader), sizeof(MessageHeader), std::back_inserter(full_buffer)); // 复制Body std::copy(inMessage.mBody.begin(), inMessage.mBody.end(), std::back_inserter(full_buffer)); mUDPSocket.async_send_to( asio::buffer(full_buffer), Remote, [](std::error_code ec, size_t inSize) { if (!ec) { std::cout << "Sent total:" << inSize << "\n"; } else { std::cout << "ERROR sending:" << ec.message() << "\n"; } std::flush(std::cout); }); }
额外注意事项
- 服务器套接字必须提前完成绑定(如
mUDPSocket.bind(asio::ip::udp::endpoint(asio::ip::udp::v4(), 6001))),且绑定操作无错误。 - 如需持续监听,需在每次接收完成后递归调用
Recieve,维持异步接收链。 - 类成员
mTemporaryMessageBuffer需考虑线程安全,避免多个异步接收操作同时修改。
内容的提问来源于stack exchange,提问作者Darshan Koirala
相关产品推荐
相关产品推荐

