- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
下面从真实代码库简化而来的 MVCE 显示了同样的问题。
服务器连续发送 5 个 UDP 帧的“突发”,其中填充了 150 个字节的值 0xA5,中间有很小的延迟或没有延迟。暂停 1 秒。
客户端使用 boost::asio async_receive_from() 函数与 1 秒定时器并行。除非 UDP 帧之间的延迟“太”小,否则客户端工作得相对较好。似乎检索到了正确的大小(此处为 150 字节),但缓冲区/vector 似乎未更新。
我尝试了大约六次来深入研究 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_service
的stopped
标志设置为false
,然后可以通过调用 来处理处理程序run_one
、one
.. 方法,您正在使用 reset
从队列中恢复处理程序。
因此我们在队列中有未处理的处理程序,在 main while 循环中创建了新 vector vec
,其所有元素都初始化为 0。async_receive_from
开始(它正在读入vec
并在 handler 中设置 ReceivedDataSize
),然后调用 reset
,run_one
可以处理 handler 并调用 handler for中止操作!并且您正在测试 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
我们正在调用 io_service::reset
但所有处理程序都已执行。从代码中删除 break
后,您可以确保所有处理程序都将执行,并保证在调用这些处理程序时本地数据有效。
关于c++ - 连续发送帧时提升asio async_receive_from()丢失udp帧数据检索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54543126/
对于通过 UDP 进行的一些数据传输,我使用了来自 boost 的 async_receive_from 函数。我的接收函数是 udp::socket socket_;
在我发现的所有样本中,我看不到任何缓冲。这是否意味着在处理程序中我总是收到完整的消息?没有分块。 我在文档中找不到任何答案。 Is boost::asio::ip::udp::socket::asyn
下面从真实代码库简化而来的 MVCE 显示了同样的问题。 服务器连续发送 5 个 UDP 帧的“突发”,其中填充了 150 个字节的值 0xA5,中间有很小的延迟或没有延迟。暂停 1 秒。 客户端使用
我在 udp_server 类中创建了异步函数 start_receive() 和 start_send()。但是通过代码不会停止并等待“async_receive_from”行。这是我的代码; cl
我正在使用 async_receive_from 读取多播输入。所以我的想法是,当我检测到间隙时,我将通知另一个帮助线程请求/获取间隙填充消息。虽然这是在工作中,但主线程将继续接收和排队任何传入的消息
在下面的代码中,asynch_receive_from 的行为是同步的。在收到数据之前它不会返回。查看最后的日志表明了这一点。这可能不是那个一开始很明显,因为有相当高的数据传入率。但是当没有数据时,只
我正在使用 boost::asio 来捕获 udp 端口上的数据包。我只是新来的。如何使 async_receive_from 复制数据以仅缓冲具有指定源 ip 的数据包? 最佳答案 根据您所说的
我有一个 boost 套接字,我在上面定义了一个异步接收: void recv() { boost_socket->async_receive_from( boost::asio::buf
class UDP_Receiver { public: udp_receiver((boost::asio::io_service& io_service) : idle_work_
我尝试使用 async_receive_from 方法来获取数据。但是当我开始时,程序不会等待超时或正在读取某些内容。我知道数据正在端口(wireshark)上传入。这可能是什么原因。我尝试使用来自
我有一个设置,多个对等方每 200 毫秒 (5fps) 广播一次 udp 数据包(包含图像)。 虽然接收本地流作为外部流在 Windows 下工作正常,但相同的代码(除了 Windows XP 中的
我正在尝试使用 Boost 库创建一个简单的异步接收器。我遵循了示例和书籍,这就是我到目前为止能够做到的: class networkUDPClient { public: utilityTr
基本上: void start_receive() { socket_.async_receive_from( boost::asio::buffer(recv_buf
我正在尝试使用 3 次握手建立异步连接。在代码的底部有一个 main 方法,它接受一个参数来确定它是充当客户端还是服务器。 如果它充当服务器,它会创建一个套接字并等待它接收到数据,然后调用回调函数 m
Boost asio 专门允许多个线程调用 io_service 上的 run() 方法。这似乎是创建多线程 UDP 服务器的好方法。但是,我遇到了一个问题,我正在努力寻找答案。 查看典型的 asyn
我有一个相当大的应用程序,可以在 Linux 上正常运行。我最近使用 VC2012 和 boost asio 1.52 在 Windows 7 上编译它并遇到了一个奇怪的问题: async_recei
我正在使用 boost::asio 创建服务器应用程序,特别是 UDP API。现在我从一个池中启动多个线程,使用 async_receive_from 监听单独的缓冲区。这在我主要开发的 Ubunt
我想创建一个自治线程,专门用于使用 boost 库 (asio) 从 UDP 套接字接收数据。这个线程应该是一个无限循环,由从 UDP 套接字接收到的一些数据触发。在我的应用程序中,我需要使用异步接收
int main() { boost::asio::io_service io_service; Worker ob1(&io_service); ob1.AsyncRead(); i
我已将 Boost 白天客户端教程中的 UDP 客户端代码修改为以下内容: class UDPClient { public: udp::socket* socket; udp::en
我是一名优秀的程序员,十分优秀!