gpt4 book ai didi

c++ - 连续发送帧时提升asio async_receive_from()丢失udp帧数据检索

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:03:46 26 4
gpt4 key购买 nike

下面从真实代码库简化而来的 MVCE 显示了同样的问题。

服务器连续发送 5 个 UDP 帧的“突发”,其中填充了 150 个字节的值 0xA5,中间有很小的延迟或没有延迟。暂停 1 秒。

客户端使用 boost::asio async_receive_from() 函数与 1 秒定时器并行。除非 UDP 帧之间的延迟“太”小,否则客户端工作得相对较好。似乎检索到了正确的大小(此处为 150 字节),但缓冲区/vector 似乎未更新。

  • 5 x 150 字节的 UDP 帧看起来并不多。
  • Wireshark 确实看到发送的完整且正确的帧。
  • 如果我使用同步升压 asio 套接字同步 receive_from(),我不会遇到任何问题

我尝试了大约六次来深入研究 boost asio,但都没有成功地找到一个单一的真相或基本原理。 SO 上的相同帖子显示非常不同的代码,因此很难将它们转换为当前代码

代码如下客户端 (client_with_timer.cc)

#include <iostream>
#include <vector>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

using namespace boost::asio;
void asyncReadHandler( const boost::system::error_code& error, std::size_t bytesTransferred );
void timeoutHandler( const boost::system::error_code& error, bool* ptime_out );

size_t ReceivedDataSize;
std::string ReadError;

int main(int argc, char * argv[])
{
io_service io;

ip::udp::socket socket(io, ip::udp::endpoint(ip::udp::v4(), 1620));

size_t num = 0;

while (true)
{
std::vector<unsigned char> vec(1500);

ip::udp::endpoint from;

socket.async_receive_from(
boost::asio::buffer( vec ),
from,
boost::bind(
asyncReadHandler,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred ) );

bool timeout = false;
ReceivedDataSize = 0;
ReadError = "";

// Creating and starting timer (by registering timeout handler)
deadline_timer timer( io, boost::posix_time::seconds( 1 ) );
timer.async_wait(
boost::bind( timeoutHandler, boost::asio::placeholders::error, &timeout ) );

// Resetting IO service instance
io.reset();

while(io.run_one())
{
if ( timeout ) {
socket.cancel();
timer.cancel();
//Leave the io run_one loop
break;
}
else if ( (0 != ReceivedDataSize ) || (!ReadError.empty())) {
timer.cancel();
socket.cancel();
std::cout << "Received n°" << num++ << ": " << ReceivedDataSize << "\r" << std::flush;

if (0 != ReceivedDataSize )
vec.resize(ReceivedDataSize);

if (!ReadError.empty())
std::cout << "Error: " << ReadError << std::endl;

bool result = true;
for ( auto x : vec )
if ( 0xA5 != x ) { result = false; break; }

if ( false == result ) {
std::cout << std::endl << "Bad reception" << std::endl << std::hex;
for ( auto x : vec )
std::cout << (int)x << " ";

std::cout << std::dec << "\n";
}
//Leave the io run_one loop
break;
}
else {
//What shall I do here ???
//another potential io.reset () did not bring much
}

}
}

return 0;
}

void asyncReadHandler( const boost::system::error_code& error, std::size_t bytesTransferred )
{
// If read canceled, simply returning...
if( error == boost::asio::error::operation_aborted ) return;

ReceivedDataSize = 0;

// If no error
if( !error ) {
ReceivedDataSize = bytesTransferred;
}
else {
ReadError = error.message();
}
}

void timeoutHandler( const boost::system::error_code& error, bool* ptime_out )
{
// If timer canceled, simply returning...
if( error == boost::asio::error::operation_aborted ) return;

// Setting timeout flag
*ptime_out = true;
}

这是服务器 (server.cc) 这样你就不用自己动手了

#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <unistd.h>

using namespace boost::asio;

int main(int argc, char * argv[])
{
io_service io;

ip::udp::socket socket(io, ip::udp::endpoint(ip::udp::v4(), 0));

std::vector<char> vec(150,0xA5);
#if 1
int separator = 1 * 1000;
#else
int separator = 0;
#endif

while (true)
{
socket.send_to(buffer(vec), ip::udp::endpoint(ip::udp::v4(), 1620));
if ( separator ) usleep(separator);
socket.send_to(buffer(vec), ip::udp::endpoint(ip::udp::v4(), 1620));
if ( separator ) usleep(separator);
socket.send_to(buffer(vec), ip::udp::endpoint(ip::udp::v4(), 1620));
if ( separator ) usleep(separator);
socket.send_to(buffer(vec), ip::udp::endpoint(ip::udp::v4(), 1620));
if ( separator ) usleep(separator);
socket.send_to(buffer(vec), ip::udp::endpoint(ip::udp::v4(), 1620));

usleep(1000*1000);
}

return 0;
}

我用下面的简单命令编译了它们:

g++ client_with_timer.cc -std=c++11 -O2 -Wall -o client_with_timer -lboost_system

g++ server.cc -std=c++11 -O2 -Wall -o server -lboost_system

当延迟太小时,它会产生如下输出

nils@localhost ASIO_C]$ ./client_with_timer 
Received n°21: 150
Bad reception
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Received n°148: 150
Bad reception
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Received n°166: 150
Bad reception
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Received n°194: 150

如何更正客户端代码以避免丢失帧?欢迎任何更好地理解 boost asio 基本原理的提示

最佳答案

我认为您的代码中存在数据竞争。如果在读取操作完成之前计时器已过期(发生超时),则执行以下代码:

if ( timeout ) {
socket.cancel();
timer.cancel();
//Leave the io run_one loop
break; // [1]
}

您正在从 while 循环中中断,socket.cancel() 取消异步读取操作,它的处理程序与 operation_aborted 错误排队等待事件循环中的处理。因为你是从 while 循环中跳转的,所以 run_one 没有被调用,这个 handler 还在队列中。

io_service.reset() 不清除队列。中止操作的处理程序仍然存在。并等待被调用。 reset() 仅将io_servicestopped 标志设置为false,然后可以通过调用 来处理处理程序run_oneone .. 方法,您正在使用 reset 从队列中恢复处理程序。

因此我们在队列中有未处理的处理程序,在 main while 循环中创建了新 vector vec,其所有元素都初始化为 0。async_receive_from 开始(它正在读入vec 并在 handler 中设置 ReceivedDataSize),然后调用 resetrun_one 可以处理 handler 并调用 handler for中止操作!并且您正在测试 ReceivedDataSizevec 中止操作...但您应该为最后启动的异步操作执行此操作。

我会将带超时的子句重写为:

if ( timeout ) {
socket.cancel();
timer.cancel();
} // no break

删除 break 后,我们保证中止的操作由 run_one 处理,并且在启动新的异步操作时没有要调用的未完成的处理程序。进行此修改后,我在测试您的代码时没有看到 bad reception

编辑

关于您的评论,是的,其他 break 语句也应从代码中删除。

程序的输出是不可预测的,因为您正在启动引用局部变量的异步操作(vecasync_receive_from 修改),处理程序排队,局部变量是销毁,稍后从 io_service 调用处理程序,而 vec 已被销毁。

您可以测试下面的代码,看看会发生什么:

  boost::asio::io_context io; // alias on io_service

boost::asio::system_timer t1{io};
t1.expires_from_now(std::chrono::seconds(1));

boost::asio::system_timer t2{io};
t2.expires_from_now(std::chrono::seconds(1));

boost::asio::system_timer t3{io};
t3.expires_from_now(std::chrono::seconds(1));

t1.async_wait ([](const boost::system::error_code& ec){ cout << "[1]" << endl;});
t2.async_wait ([](const boost::system::error_code& ec){ cout << "[2]" << endl;});
t3.async_wait ([](const boost::system::error_code& ec){ cout << "[3]" << endl;});
// 3 handlers are queueud
cout << "num of handlers executed " << io.run_one() << endl; // wait for handler, print 1
io.reset(); // RESET is called
cout << "num of handlers executed " << io.run_one() << endl; // wait for handler, print 1
io.reset(); // RESET is called
cout << "num of handlers executed " << io.run_one() << endl; // wait for handler, print 1
cout << "executed: " << io.poll_one() << endl; // call handler if any ready, print 0

我们正在调用 io_service::reset 但所有处理程序都已执行。从代码中删除 break 后,您可以确保所有处理程序都将执行,并保证在调用这些处理程序时本地数据有效。

关于c++ - 连续发送帧时提升asio async_receive_from()丢失udp帧数据检索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54543126/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com