我正在使用Boost ASIO实现HTTP文件下载客户端。我正在使用async_read操作。我面临的问题是在async_read中,我在收到完整内容(即Content-Length)之前收到了EOF,无论内容大小如何,都会发生这种情况。鉴于是我的阅读操作
void Http::ResumableAsyncDownload::read_content(const boost::system::error_code& err, size_t _size)
{
try {
if ( !err)
{
received_bytes += _size;
if ( ofs_.is_open() ) {
// Write all of the data that has been read so far.
ofs_ << &response_;
} else {
ofs_.open(std::string(params_.tempdir + "/" + params_.partnumber).c_str());
if ( ofs_.is_open() ) {
ofs_ << &response_;
} else {
DEBUG_MSG("Error while opening file to store downloaded data. File Path = %s\n", std::string(params_.tempdir + "/" + params_.partnumber).c_str());
std::cout << ("Unable to open local file for storing downloaded data");
}
}
// Continue reading remaining data until EOF.
boost::asio::async_read(*ssocket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&ResumableAsyncDownload::read_content, this, _1, _2)
);
}
else if (err != boost::asio::error::eof)
{
DEBUG_MSG("[NET] : Exception in ResumableAsyncDownload in read_content : %s\n", err.message().c_str());
std::cout << ("Asynchronous File Download Error: " + err.message());
}
if(err == boost::asio::error::eof)
{
std::cout << "[RESPONSE] : EOF: We are not breaking connection\n";
ssocket_->shutdown();
delete ssocket_;
ssocket_ = NULL;
delete ctx;
ctx = NULL;
if ( (content_length != received_bytes) && !(params_.get_size) ) {
std::cout << "Failed to receive complete data packet. Content Length = " << content_length << " Received Bytes = " << received_bytes << std::endl;
// ofs_.clear();
}
}
} catch ( std::exception &ex ) {
std::cout << "We have an exception. Exception = " << std::string(ex.what()) << std::endl;
}
}
例如,Content-Length是292309324,但我会在292309324之前收到EOF。
为了克服这个问题,我已经使用HTTP Range标头实现了Chunked下载,但是在那种情况下,我请求的每个chunk都收到的请求数少于请求的chunk,然后我重新计算下一个范围,它在最后一个之前工作块。我从来没有收到最后一块,通常是情况(即)
Range for last chunk 227376464-227376641/227376641
Requested Bytes = 178
响应标头
X-Powered-By: Undertow/1
Content-Range: bytes 227376464-227376641/227376641
Server: WildFly/9
Content-Length: 178
Accept-Ranges: bytes
OperationId: 4a847024-2348-42bd-af7d-3638e41cba4f
Date: Thu, 17 Aug 2017 11:41:18 GMT
Set-Cookie: SERVERID=04-84FRD2128G0US; path=/
Cache-control: private
正如您所看到的,服务器正在响应最后一个块的良好范围,但是read_content正在给出EOF。
因此,在两种方法中,read_content都不会读取完整数据并给出EOF。据我所知,EOF是由服务器关闭的套接字,也可能导致短读,但不是我的分块下载解决方案。我不应该完全接收最后一个chunked数据包。
关于什么是错误的任何想法?另请注意,我正在调用自定义API来下载文件,但即使我从某个公共链接(即http://mirror.pnl.gov/releases/16.04/ubuntu-16.04.3-desktop-amd64.iso)下载,我也会看到相同的问题,所以我认为问题不在我的服务器端。另请注意,如果我使用boost :: asio :: async_read的同步版本(即boost :: asio :: read),我不会看到这个问题。我正在使用为ARM编译的Boost版本1.55。
答案 0 :(得分:0)
我建议为Http
班size_t bytes_transferred_total_;
添加总字节数,然后放弃err == boost::asio::error::eof
。您知道总大小,因为它是您已解析的原始HTTP标头Content-Length: <total_body_size>
的一部分。修改后的代码看起来像这样:
...
if (!err) {
received_bytes += _size;
bytes_transferred_total_ += _size;
...
} else {
...
}
boost::asio::async_read(
*ssocket_, response_, boost::asio::transfer_at_least(1),
boost::bind(&ResumableAsyncDownload::read_content, this, _1, _2));
// continue reading until all data has been received
if (bytes_transferred_total_ >= content_length_from_header_) {
// you've received it all
}
在初始化content_length_from_header_
时,请确保您了解它代表了正文的大小,不包括标题。
脚注:考虑使用lambdas而不是boost::bind
或std::bind
,因为它们通常更具性能,例如允许编译器内联它们。当然,除非你需要利用bind
的动态性。