boost asio async_receive_from()在连续发送帧时缺少udp帧数据检索
boost asio async_receive_from() missing udp frames data retrieval when frames sent consecutive
下面从实际代码库简化的MVCE显示了相同的问题。
服务器连续发送5个UDP帧的"突发",其中填充了值为0xA5的150字节,其间有很小的延迟或没有延迟。暂停1秒。
客户端使用boost::asioasync_receive_from()函数与1秒计时器并行。客户端工作相对良好,除非UDP帧之间的延迟"太小"。似乎检索到了正确的大小(此处为150字节),但缓冲区/矢量似乎没有更新。
- 5 x 150字节的UDP帧似乎不多
- Wireshark确实看到了发送的完整且正确的帧
- 如果我使用同步boost asio套接字synchronous 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-墙-o client_with_timer-lbostrongystem
g++服务器.cc-std=c++11-O2-墙-o服务器-lhostrongystem
当延迟太小时,它会产生如下输出
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
如何更正客户端代码以避免漏帧?欢迎任何关于更好地理解助推asio原理的提示
我认为您的代码中存在数据竞赛。如果在读取操作完成之前计时器已过期(发生超时),则执行以下代码:
if ( timeout ) {
socket.cancel();
timer.cancel();
//Leave the io run_one loop
break; // [1]
}
您正在从while循环中断,socket.cancel()
取消异步读取操作,其带有operation_aborted
错误的处理程序将排队并等待事件循环中的处理。因为您是从while循环跳出来的,所以run_one
不会被调用,并且这个处理程序仍在队列中。
io_service.reset()
不清除队列。中止操作的处理程序仍然存在。并等待调用。reset()
仅将io_service
的stopped
标志设置为false
,然后可以通过调用run_one
、one
来处理处理程序。。方法,您正在使用reset
从队列中恢复处理处理程序。
所以我们在队列中有未处理的处理程序,主要是在创建循环新向量vec
时,它的所有元素都初始化为0。async_receive_from
启动(它正在读取vec
并在其处理程序中设置ReceivedDataSize
),然后调用reset
,run_one
可以处理处理程序并调用处理程序进行中止操作!并且您正在测试ReceivedDataSize
和vec
的中止操作。。。但是您应该为上次启动的异步操作执行此操作。
我会重写超时条款为:
if ( timeout ) {
socket.cancel();
timer.cancel();
} // no break
在删除break之后,我们保证中止的操作由run_one
处理,并且在启动新的异步操作时不会调用任何未完成的处理程序。在这次修改之后,我在测试您的代码时没有看到bad reception
。
编辑
关于您的评论,是的,其他break
语句也应该从代码中删除。
程序的输出是不可预测的,因为您正在启动异步操作,该操作引用本地变量(vec
由async_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
我们正在调用CCD_ 26,但是所有的处理程序都被执行。从代码中删除break
之后,您可以确保执行所有处理程序,并保证在调用这些处理程序时本地数据是有效的。
- 从udp接收帧对于人脸识别来说太慢
- 使用VerQueryValue检索应用程序的文件描述
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 是否可以从格式字符串中检索"width"
- 使用 pqxx 将 std::vector 存储在 postgresql 中,并从数据库中检索它
- 如何使用 Boost Asio 在 Android 上获取我的本地 udp IP 地址?
- boost::asio UDP 广播客户端仅接收"fast"数据包
- (Winsock) UDP 接收工作正常,但同一套接字的发送失败
- 如何在QByteArray中放置和检索位字段而不会感到痛苦?
- 如何在Qt中从数据库中检索二进制数据?
- NodeJs 服务器充斥着 UDP 广播,不发送响应
- 如何通过UDP接收QByteArray并将其解析为位字段结构?
- 发送固定大小的 UDP 数据包
- 如何从C++代码中检索 QML 的文本字段中的文本?
- 从 opencv c++ 中的矢量中检索固定的帧数
- SIGSEGV on Boost UDP 套接字关闭 - tcache_get at malloc.c.
- 如何在不等待检索的情况下获取C++中的内存位置?
- 使用 UDP 中断 while()-循环
- 如何在 QTreeWidget 中检索特定项目的 mimeData?
- boost asio async_receive_from()在连续发送帧时缺少udp帧数据检索