boost :: asio :: async_read在收到完整的Content-Length之前接收EOF

时间:2017-08-17 12:15:55

标签: c++ sockets boost boost-asio

我正在使用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。

1 个答案:

答案 0 :(得分:0)

我建议为Httpsize_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::bindstd::bind,因为它们通常更具性能,例如允许编译器内联它们。当然,除非你需要利用bind的动态性。