BOOST ASIO :: DENALLINE_TIMER在超时之前重置
Boost asio::deadline_timer is resetting before timeout
我正在使用boost :: asio :: deadline_timer添加套接字超时选项。我已经实现了异步HTTP读取的读取,并且当我开始与服务器连接时启动deadline_timer,并且在每个回调上,我都会使用功能deadline_timer :: expires_from_now重置deadline_timer。在deadline_timer的错误处理程序中,我清楚地检查了超时是实际的还是操作_abort。但是几乎总是我甚至在预期超时之前就会收到实际的超时。请看看我给定的代码。我在每个回调中都不理解我正在重置计时器,所以为什么会遇到此超时错误。
#define TCP_SOCKET_TIMEOUT 10
Http::AsyncDownload::AsyncDownload(boost::asio::io_service& io_service,
const std::string &protocol,
const std::string &serverip,
const std::string &port,
const std::string &path,
const std::string &filename,
const std::string &localFilePath,
const std::string &username,
const std::string &password) :
resolver_(io_service),
socket_(io_service),
timer_(io_service, boost::posix_time::seconds(TCP_SOCKET_TIMEOUT)),
localFilePath_(localFilePath),
downloadFile_(filename),
protocol_(protocol)
{
........
// Start TCP Socket Timer
start_socket_timer();
// Start an asynchronous resolve to translate the server and service names
// into a list of endpoints.
tcp::resolver::query query(serverip, port);
resolver_.async_resolve(query, boost::bind(&AsyncDownload::resolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator)
);
}
void Http::AsyncDownload::resolve(const boost::system::error_code &err,
tcp::resolver::iterator endpoint_iterator)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
.........
boost::asio::async_connect(ssocket_->lowest_layer(), endpoint_iterator, boost::bind(&AsyncDownload::connect, this, boost::asio::placeholders::error));
} else {
// Error handling here
}
}
void Http::AsyncDownload::connect(const boost::system::error_code& err)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
.........
boost::asio::async_write(socket_, request_,
boost::bind(&AsyncDownload::write_request, this, boost::asio::placeholders::error));
}
else {
// Error handling here
}
}
void Http::AsyncDownload::hand_shake(const boost::system::error_code& err)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
.........
boost::asio::async_write(*ssocket_, request_,
boost::bind(&AsyncDownload::write_request, this,
boost::asio::placeholders::error));
} else {
// Error handling here.
}
}
void Http::AsyncDownload::write_request(const boost::system::error_code& err)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
.............
boost::asio::async_read_until(*ssocket_, response_, "rn",
boost::bind(&AsyncDownload::read_status_line, this,
boost::asio::placeholders::error));
} else {
// Error handling here
}
}
void Http::AsyncDownload::read_status_line(const boost::system::error_code& err)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
..........
boost::asio::async_read_until(*ssocket_, response_, "rnrn",
boost::bind(&AsyncDownload::read_headers, this,
boost::asio::placeholders::error));
} else {
// Error handling here.
}
}
void Http::AsyncDownload::read_headers(const boost::system::error_code& err)
{
refresh_socket_timer();
if ( !err ) {
..............
boost::asio::async_read(*ssocket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&AsyncDownload::read_content, this,
boost::asio::placeholders::error)
);
} else {
// Error handling here
}
}
void Http::AsyncDownload::read_content(const boost::system::error_code& err)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
boost::asio::async_read(*ssocket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&AsyncDownload::read_content, this,
boost::asio::placeholders::error)
);
} else if ( err != boost::asio::error::eof ) {
// Error handling here.
} else {
// We have an EOF
}
}
void Http::AsyncDownload::start_socket_timer()
{
timer_.async_wait(boost::bind(&Http::AsyncDownload::socket_timeout, this,
boost::asio::placeholders::error));
}
void Http::AsyncDownload::refresh_socket_timer()
{
timer_.expires_from_now(boost::posix_time::seconds(TCP_SOCKET_TIMEOUT));
timer_.async_wait(boost::bind(&Http::AsyncDownload::socket_timeout, this, boost::asio::placeholders::error));
}
void Http::AsyncDownload::socket_timeout(const boost::system::error_code &error_)
{
// operation_aborted error is thrown whenever we cancel the timer or
// we reset the timer using expires_from_now function,
if ( error_ != boost::asio::error::operation_aborted ) {
csputils::Utils::DEBUG_MSG(downloadFile_, __LINE__, " ------------> TCP Connection Timeout. Broken Connection Found. Abort Operation ");
// Ok, our TCP connection is broken, we will cancel all asynchronous
// operations of our sockets.
ssocket_->shutdown(); // For Secure Socket & socket.close(); for normal socket.
} else {
// Ok, we have reset the timer, please continue...
}
}
好的。在上面的代码中,您会注意到我正在构造函数中启动计时器,一旦收到一个数据包,我将使用expries_from_now函数调用来刷新计时器。此呼叫将带有错误代码操作的呼叫错误处理程序(socket_timeout),但对于每个实际超时,此函数都会在没有操作_abort的情况下调用,您可以看到我正在明确检查aperation_aborted,但是按照我的期望,我仍然会尽早收到超时,但是我正在收到的每个数据包上的计时器刷新计时器,但我敢肯定它在10秒之前已过期。我还尝试了超时值= 60,但效果相同。任何想法。
更新我在实际代码中使用的错误处理更新了我的代码。为了简单起见,我已经修剪了实际的代码。您可以在计时器超时处理程序中注意到,我正在检查超时是否没有明确(即操作_abort),然后关闭套接字。插座关闭后,我将在套接字数据处理程序(主要是read_content函数)中获得例外。在此功能中,当我收到异常时,我的插座将退出调用ASYNCDOWNLOAD DESTRUCTOR,在此进行更多清洁。如果我删除deadline_timer,我的下载代码非常完美。我在此处添加了它以检测不可预见的TCP连接下降。我在Arm Architture上的嵌入式Linux上运行此代码,并且我的插座很安全,但是当我提到的没有计时器的情况下,我的代码正常工作,因此我认为问题与插座无关。
好吧,所以,我已经玩过你的榜样。
我可以看到超时到期时发生的无限循环,因为缺乏错误处理,尽管已经到达了超时,但read_content
循环仍继续进行。
注意:
-
第一个
read_until_async
建议它只会读取状态线。这根本不是那么回事!请参阅boost read_until在定界线上不会停止。它将读取第一个数据的" swoop",其中包括至少
rn
。实际上,许多服务器将标题发送给同一数据包。(实际上,有效的行为可能取决于代理和可能的硬件)。因此,假设您应该始终要求第二个read_until_async
阅读标题是不安全的。而且由于可能永远不会发生"rnrn"
,因此很容易进入故障模式(例如,到达流程末端时)。 -
如果您不仔细观察流程,则可以得出结论:"计时器发射得太早"(例如,您降落在
refresh_socket_timer
中)。但是,在我的错误情况下正在发生的事情是read_until_async
只是立即返回,这是的,就像返回数据包一样。
所以我提出了
之类的东西void read_content(const boost::system::error_code& err)
{
if (err && socket_.is_open())
{
DEBUG_TRACE();
boost::asio::async_read(*ssocket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&AsyncDownload::read_content, this,
boost::asio::placeholders::error)
);
refresh_socket_timer();
}
else
std::cerr << "Error '" << err.message() << "'n";
}
因此,我们避免了连接下降时的无限循环。
void refresh_socket_timer()
{
if (socket_.is_open())
{
DEBUG_TRACE();
timer_.expires_from_now(boost::posix_time::seconds(TCP_SOCKET_TIMEOUT));
timer_.async_wait(boost::bind(&AsyncDownload::socket_timeout, this, boost::asio::placeholders::error));
}
}
因此,我们避免关闭插座后刷新计时器。
void socket_timeout(const boost::system::error_code &error_)
{
// operation_aborted error is thrown whenever we cancel the timer or
// we reset the timer using expires_from_now function,
if ( error_ != boost::asio::error::operation_aborted ) {
DEBUG_TRACE();
std::cout << " ------------> TCP Connection Timeout. Broken Connection Found. Abort Operationn";
// Ok, our TCP connection is broken, we will cancel all asynchronous
// operations of our sockets.
socket_.close();
}
}
因此,我们在超时时积极关闭插座。
如果您在上述条件下评论if(...)
条件,则会看到我所描述的故障模式。
这是我用来测试的完整示例:
#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/bind.hpp>
using tcp = boost::asio::ip::tcp;
#define TCP_SOCKET_TIMEOUT 2
#define DEBUG_TRACE() do { std::cout << __FILE__ << ':' << __LINE__ << "t" << __FUNCTION__ << "n"; } while(false)
struct AsyncDownload
{
tcp::resolver resolver_;
tcp::socket socket_;
tcp::socket* ssocket_ = &socket_;
boost::asio::deadline_timer timer_;
std::string localFilePath_;
boost::asio::streambuf response_;
AsyncDownload(boost::asio::io_service& io_service,
const std::string &protocol,
const std::string &serverip,
const std::string &port) :
resolver_(io_service),
socket_(io_service),
timer_(io_service, boost::posix_time::seconds(TCP_SOCKET_TIMEOUT))
{
DEBUG_TRACE();
// Start TCP Socket Timer
start_socket_timer();
// Start an asynchronous resolve to translate the server and service names
// into a list of endpoints.
tcp::resolver::query query(serverip, port);
resolver_.async_resolve(query, boost::bind(&AsyncDownload::resolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator)
);
}
void resolve(const boost::system::error_code &err, tcp::resolver::iterator endpoint_iterator)
{
DEBUG_TRACE();
boost::asio::async_connect(ssocket_->lowest_layer(), endpoint_iterator, boost::bind(&AsyncDownload::connect, this, boost::asio::placeholders::error));
refresh_socket_timer();
}
void connect(const boost::system::error_code& err)
{
DEBUG_TRACE();
std::string const request_ = "GET / HTTP/1.1rnHost: www.example.comrnrn";
boost::asio::async_write(socket_, boost::asio::buffer(request_),
boost::bind(&AsyncDownload::write_request, this, boost::asio::placeholders::error));
refresh_socket_timer();
}
void write_request(const boost::system::error_code& err)
{
DEBUG_TRACE();
boost::asio::async_read_until(*ssocket_, response_, "rn",
boost::bind(&AsyncDownload::read_status_line, this,
boost::asio::placeholders::error));
refresh_socket_timer();
}
void read_status_line(const boost::system::error_code& err)
{
DEBUG_TRACE();
std::cout << "read_status_line: " << &response_ << "n";
boost::asio::async_read_until(*ssocket_, response_, "rnrn",
boost::bind(&AsyncDownload::read_headers, this,
boost::asio::placeholders::error));
refresh_socket_timer();
}
void read_headers(const boost::system::error_code& err)
{
DEBUG_TRACE();
// ..............
boost::asio::async_read(*ssocket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&AsyncDownload::read_content, this,
boost::asio::placeholders::error)
);
refresh_socket_timer();
}
void read_content(const boost::system::error_code& err)
{
if (err && socket_.is_open())
{
DEBUG_TRACE();
boost::asio::async_read(*ssocket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&AsyncDownload::read_content, this,
boost::asio::placeholders::error)
);
refresh_socket_timer();
}
else
std::cerr << "Error '" << err.message() << "'n";
}
void start_socket_timer()
{
DEBUG_TRACE();
timer_.async_wait(boost::bind(&AsyncDownload::socket_timeout, this, boost::asio::placeholders::error));
}
void refresh_socket_timer()
{
if (socket_.is_open())
{
DEBUG_TRACE();
timer_.expires_from_now(boost::posix_time::seconds(TCP_SOCKET_TIMEOUT));
timer_.async_wait(boost::bind(&AsyncDownload::socket_timeout, this, boost::asio::placeholders::error));
}
}
void socket_timeout(const boost::system::error_code &error_)
{
// operation_aborted error is thrown whenever we cancel the timer or
// we reset the timer using expires_from_now function,
if ( error_ != boost::asio::error::operation_aborted ) {
DEBUG_TRACE();
std::cout << " ------------> TCP Connection Timeout. Broken Connection Found. Abort Operationn";
// Ok, our TCP connection is broken, we will cancel all asynchronous
// operations of our sockets.
socket_.close();
}
}
};
int main()
{
DEBUG_TRACE();
boost::asio::io_service io_service;
AsyncDownload download(
io_service,
"http",
"www.google.com",
"80");
io_service.run();
}
- C++减少modbus_connect超时
- 没有超时的C++条件变量
- 如何在C++(VS2010)中设置超时读取USB端口?
- C++卷曲柱在curl_easy_perform超时
- 如何在Qt 4.8中阻止/忽略/丢弃早于特定超时的用户输入事件
- 如何改进我的代码,使其不会因超时而终止?(黑客排名挑战)
- C++:函数外部的超时功能
- 使用boost::multiprecision cpp_int左移时出现超时错误
- QTimer 超时不会在单元测试中触发
- 使用单体计时器的pthread_cond_timedwait有时会比预期晚超时
- 超时后,线程睡眠不会继续执行
- 接受超时的 TCP 连接
- 为 g++ build 设置超时,为 Sublime Text 运行?
- 是否需要在 Timer.5 boost::asio 教程中运行新线程?
- 如何通过命令行工具在 COIN-OR Cbc 中设置超时?
- 高CPU使用率,在API桌面复制中获取帧之间具有不同的超时间隔
- libevent是否允许在不同的线程中运行timer/io的回调
- 0作为std::condition_variable::wait_fo中的超时
- 为什么我在蓝牙连接()上收到"java.io.IOException:读取失败,套接字可能关闭或超时,读取re
- UDP 套接字 select() 在某些情况下无延迟(超时)返回 1