使用boost asio下载文件时出现问题,输出文件中出现垃圾

Problem downloading files with boost asio, junk in output file

提问人:bergemon 提问时间:10/18/2023 最后编辑:John Kugelmanbergemon 更新时间:10/18/2023 访问量:56

问:

我正在尝试从服务器下载任何文件 - 图像、文本/html、zip 等。但不知何故,我在输出文件的开头和结尾有一些垃圾。

#include "dependencies.h"

asio::streambuf response_;
std::istream is(&response_);
std::ofstream file("file.html", std::ios::out);

void readHeader(asio::ssl::stream<tcp::socket>& socket);
void asyncRead(asio::ssl::stream<tcp::socket>& socket);

void readStatus(asio::ssl::stream<tcp::socket>& socket) {

    asio::async_read_until(socket, response_, "\r\n", [&](boost::system::error_code ec, size_t length) {
        if (!ec) {
            std::string header;
            is >> header;
            std::cout << "Protocol: " << header << '\n';
            std::string status_code;
            std::string status;
            is >> status_code;
            std::cout << "Status: " << status_code << '\n';
            std::string status_message;
            std::getline(is, status_message);

            readHeader(socket);
        }
        else if (ec != asio::error::eof) {
            std::cout << "[Status code] Error: " << ec.message() << '\n';
        }
    });
}

void readHeader(asio::ssl::stream<tcp::socket>& socket) {

    asio::async_read_until(socket, response_, "\r\n\r\n", [&](boost::system::error_code ec, size_t length) {
        if (!ec) {
            std::string status_message;
            while (getline(is, status_message) && status_message != "\r")
                std::cout << status_message << '\n';

            asyncRead(socket);
        }
        else if (ec != asio::error::eof) {
            std::cout << "[Header message] Error: " << ec.message() << '\n';
        }
        });
}

void asyncRead(asio::ssl::stream<tcp::socket>& socket) {
    asio::async_read(socket, response_, asio::transfer_at_least(1), [&](const boost::system::error_code ec, size_t length) {
        if (!ec) {
            file << &response_;

            asyncRead(socket);
        }
        else if (ec != asio::error::eof) {
            std::cout << "[Async reading] Error: " << ec.what() << std::endl;
        }
    });
}

int main(int args, const char* argv[]) {

    setlocale(LC_ALL, "Rus");

    boost::system::error_code ec;
    asio::io_context context;
    asio::ssl::context sslContext(asio::ssl::context::method::sslv23_client);
    sslContext.set_default_verify_paths();
    asio::ssl::stream<tcp::socket> socket(context, sslContext);

    //https://codeload.github.com/ERHZAN/NRM-Launcher/zip/refs/heads/main
    boost::urls::url url = boost::urls::url_view("https://en.cppreference.com/w/");
    std::string path = url.path();
    std::string host = url.host();
    std::string scheme = url.scheme();

    asio::streambuf request_;
    std::ostream os(&request_);
    os << "GET " << path << " HTTP/1.1" << "\r\n";
    os << "Host: " << host << "\r\n";
    os << "Accept: */*\r\n";
    os << "Connection: close\r\n\r\n";

    tcp::resolver resolver(context);
    tcp::resolver::query query(host, "https");
    resolver.async_resolve(query, [&](const boost::system::error_code& ec, tcp::resolver::iterator ep_iterator) {
        if (!ec) {
            socket.set_verify_mode(boost::asio::ssl::verify_peer);
            socket.set_verify_callback([&](bool preverified, boost::asio::ssl::verify_context& ctx) {
                char subject_name[256];
                X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
                X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
                std::cout << "Verifying " << subject_name << "\n";

                return true;
            });

            socket.lowest_layer().async_connect(ep_iterator->endpoint(), [&](const boost::system::error_code& ec) {
                if (!ec) {
                    socket.async_handshake(asio::ssl::stream_base::handshake_type::client, [&](const boost::system::error_code& ec) {
                        if (!ec) {
                            asio::async_write(socket, request_, [&](const boost::system::error_code ec, size_t length) {
                                if (!ec) {
                                    readStatus(socket);
                                }
                                else {
                                    std::cout << ec.what() << std::endl;
                                }
                            });
                        }
                        else {
                            std::cout << ec.what() << std::endl;
                        }
                    });
                }
                else {
                    std::cout << "[Connection] Error: " << ec.what() << std::endl;
                }
                });
        }
    });
    context.run();

    return 0;
}

我的输出文件是:

ab5b

<!DOCTYPE html>
<html lang="en" dir="ltr" class="client-nojs">
i've deleted all html here for your convenience
</html>


0




C++ 套接字提升 ASIO

评论


答:

1赞 Nick Matteo 10/18/2023 #1

这是块编码

每个块前面都有其大小(以字节为单位)。传输端 当收到长度为零的块时。chunked 关键字 Transfer-Encoding 标头用于指示分块传输。

[...]

每个块都从它嵌入的数据的八位字节数开始 表示为 ASCII 中的十六进制数,后跟可选 参数(块扩展)和终止 ␍␊ 序列,后跟 通过块数据。该块以 ␍␊ 结尾。

也许可以尝试一个处理 HTTP 的库,例如 Boost Beast,它会为您处理这个问题。或者尝试如何告诉 HTTP 服务器不发送分块编码中的解决方案:即指定 HTTP/1.0。