提问人:bergemon 提问时间:10/18/2023 最后编辑:John Kugelmanbergemon 更新时间:10/18/2023 访问量:56
使用boost asio下载文件时出现问题,输出文件中出现垃圾
Problem downloading files with boost asio, junk in output file
问:
我正在尝试从服务器下载任何文件 - 图像、文本/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
答:
1赞
Nick Matteo
10/18/2023
#1
这是块编码。
每个块前面都有其大小(以字节为单位)。传输端 当收到长度为零的块时。chunked 关键字 Transfer-Encoding 标头用于指示分块传输。
[...]
每个块都从它嵌入的数据的八位字节数开始 表示为 ASCII 中的十六进制数,后跟可选 参数(块扩展)和终止 ␍␊ 序列,后跟 通过块数据。该块以 ␍␊ 结尾。
也许可以尝试一个处理 HTTP 的库,例如 Boost Beast,它会为您处理这个问题。或者尝试如何告诉 HTTP 服务器不发送分块编码中的解决方案:即指定 HTTP/1.0。
评论