【发布时间】:2014-01-30 10:50:29
【问题描述】:
我正在使用 boost::asio::deadline_timer 添加套接字超时选项。我已经实现了异步 HTTP 读取,并在开始与服务器连接时启动了 deadtime_timer,并且在每次回调时,我都使用函数deadline_timer::expires_from_now 重置了deadline_timer。在deadline_timer 的错误处理程序中,我清楚地检查超时是实际的还是操作中止的。但我几乎总是在预期超时之前收到实际超时。请看一下我给定的代码。我不明白在每个回调中我都在重置计时器,那么为什么我会收到这个超时错误。
#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_, "\r\n",
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_, "\r\n\r\n",
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 函数调用刷新计时器。此调用将使用错误代码 operation_aborted 调用错误处理程序 (socket_timeout),但对于每个实际超时,将在没有 operation_aborted 的情况下调用此函数,您可以看到我正在显式检查 operation_aborted,但仍然按照我的预期,虽然我会提前收到超时我正在刷新收到的每个数据包的计时器,但我确信它会在 10 秒前过期。我也尝试了超时值 = 60,但效果相同。任何想法。
更新 用我在实际代码中使用的错误处理更新了我的代码。为了简单起见,我已经精简了我的实际代码。您可以在计时器超时处理程序中注意到,我正在检查超时是否不明确(即 operation_aborted),然后关闭套接字。一旦套接字关闭,我的套接字数据处理程序(主要是 read_content 函数)中会出现异常。在那个函数中,当我收到异常时,我的套接字将退出调用 AsyncDownload 析构函数,我正在做更多的清理工作。如果我删除了deadline_timer,我的下载代码就可以完美运行。我已在此处添加它以检测不可预见的 TCP 连接丢失。我在 ARM 架构上的嵌入式 Linux 上运行此代码,并且我的套接字是安全的,但正如我所提到的,我的代码在没有计时器的情况下也能完美运行,所以我认为问题与套接字无关。
【问题讨论】: