Boost C++ UDP Socket在Windows下接收133个包后停止的求助
我通过服务器向客户端发送UDP数据包,服务器将数据拆分为500字节的包发送,客户端接收后累加数据并反序列化为对象。目前遇到的问题是:Windows客户端最多接收133个包后就停止接收,仿佛没有更多数据包发到Socket,但服务器实际发送了完整的1238个包;该问题仅在Windows下出现,OSX环境运行完全正常。
服务器发送数据包代码
// sends @buffer of size @length to @endpoint // @buffer already contains a header, and the method splits @buffer into chunks and send it one by one void server::send_package(char* buffer, int length, udp::endpoint endpoint){ if (length > BUFFER){ protocol::header header; int dataLength = length - sizeof (header); // copy header from buffer memcpy(&header, buffer, sizeof(header)); header.isEnd = false; int position = 0; // allocate memory to collect data to send char* data_to_send = new char[dataLength]; // copy data memcpy(data_to_send, &buffer[sizeof(header)], dataLength); header.totalPackages = dataLength/(BUFFER-sizeof (header)); // create chucks of data and send while (position < dataLength){ int frame_size = BUFFER; header.currentPackage++; if (dataLength-position+sizeof (header) <= BUFFER) { header.isEnd = true; frame_size = dataLength-position+sizeof (header); } char* temp_buffer = new char[frame_size]; header.length = frame_size-sizeof(header); // set the header of a chunk memcpy(temp_buffer, &header, sizeof(header)); // set data to chunk memcpy(&temp_buffer[sizeof (header)], &data_to_send[position], frame_size-sizeof(header)); // send chunk socket->send_to(boost::asio::buffer(temp_buffer, frame_size), endpoint); socket->wait(boost::asio::ip::tcp::socket::wait_write); position += frame_size-sizeof(header); } } else { socket->async_send_to(boost::asio::buffer(buffer, length), endpoint, boost::bind(&server::release_sent_buffer, this, buffer, length) ); } }
客户端接收数据包代码
void connectionManager::handle_receive( const boost::system::error_code &error, std::size_t size, udp::endpoint* ep) { if (size > 0) { // _lock.try_lock(); protocol::header header; memcpy(&header, &recv_buffer, sizeof(header)); logg("response from server received " + boost::asio::ip::address_v4(header.ip).to_string()); logg("received header:"); logg(protocol::getHeaderInfo(header)); std::stringstream ss; ss << "header.length = " << header.length; logg(ss.str().c_str()); udp::endpoint endpoint(boost::asio::ip::address_v4(header.ip), _server_port); switch (header.command) { case protocol::commands::server_instance_instruments_state_response: { package_chain chain(size-sizeof(header)); memcpy(chain.data, &recv_buffer[sizeof(header)], size-sizeof(header)); packages[header.id].push_back(chain); // at Windows machine the last package is #133. But 1248 packages expected. // WHY????... int packs = (packages[header.id].size()); if (header.isEnd) { char* buf = getDataFromPackages(header.id, header.length); std::stringstream str; str << buf; boost::archive::text_iarchive ar(str); instance_plugin_information* inst_inf; inst_inf = new instance_plugin_information(); try { ar & inst_inf; if (onPluginStateResponse != nullptr) { onPluginStateResponse(*inst_inf); } } catch (const std::exception& e) { } } break; } } // We will hang on this line when package #133 received. socket->wait(boost::asio::ip::tcp::socket::wait_read); connectionManager::start_receive(); }
问题排查与修复建议
修正Socket等待类型
客户端代码中错误使用了boost::asio::ip::tcp::socket::wait_read,但实际是UDP Socket,Windows对这种类型不匹配的兼容性极差,直接导致wait调用卡住。应改为udp::socket::wait_read。调大UDP接收缓冲区
Windows默认UDP接收缓冲区远小于OSX,服务器高速发送时缓冲区很快溢出,后续数据包被直接丢弃。可以手动设置更大的缓冲区:boost::asio::socket_base::receive_buffer_size option(10 * 1024 * 1024); // 设置为10MB socket->set_option(option);控制服务器发送速率
服务器当前无间隔连续发送,Windows网络栈无法处理高密度UDP包。可以在每次send_to后添加微小延时,或改用异步发送配合io_context调度:// 替换同步send_to和wait,改用异步发送 socket->async_send_to(boost::asio::buffer(temp_buffer, frame_size), endpoint, [this, temp_buffer](const boost::system::error_code& ec, std::size_t) { delete[] temp_buffer; // 发送完成后释放内存 } ); io_context.run_one(); // 调度异步任务,避免阻塞修复内存泄漏
服务器代码中data_to_send和temp_buffer使用new分配后未释放,长期运行会导致内存泄漏,间接影响稳定性。需在循环结束后delete[] data_to_send,异步发送的temp_buffer在回调中释放。修正分包计算逻辑
服务器中header.totalPackages = dataLength/(BUFFER-sizeof (header))未处理余数情况,若dataLength不能被单包数据大小整除,会导致分包数量计算错误。应改为:header.totalPackages = (dataLength + (BUFFER - sizeof(header)) - 1) / (BUFFER - sizeof(header));
内容的提问来源于stack exchange,提问作者Dmitry

