我正在开发一个程序,需要从
boost::asio::ssl::stream<boost::beast::tcp_stream>
等 http 网页读取所有数据。
我尝试阅读直到 EOF 有效,但是它依赖于服务器关闭连接,这在发送大量请求时效率非常低(这对于我的程序设计使用的上下文可能很常见),所以理想情况下 id 像寻找另一种方法。
我尝试使用内容长度标头,但它不可靠,因为服务器不强制使用它,我对使用“tcp 帧”感兴趣,但我不知道如何与 boost 一起使用
我也尝试过
Stream->async_read_some(boost::asio::buffer(*buff, 1448) boost::bind(blahblah))
并阅读,直到我得到低于1448的bytes_transferred
,但这种方法似乎不起作用。
注意事项: 使用
socket_base::bytes_readable
似乎只能用于获取加密数据的大小。
由于要处理无数的文件类型,因此无法继续阅读
\r\n\r\n
。
非常感谢。 <3
您必须实现 HTTP 协议。
为此,您可以使用在 Asio 之上实现的 Boost Beast 库。它确实实现了您所说的无数传输编码,包括对 HTTP/1.1 功能的支持,例如
Connection: keepalive
、分块编码和可选的内容长度。
这是一个简单的客户端示例:
int main(int argc, char** argv) {
std::map<url, std::vector<char> > downloads;
for (url u : std::vector<url>(argv + 1, argv + argc)) {
std::cout << "Downloading " << u << std::endl;
auto& [sock, buffer] = connect(u);
// download from the url target using HTTP GET
{
http::request<http::empty_body> req{http::verb::get, u.encoded_resource().decode(), 11};
req.set(http::field::host, u.host());
req.set(http::field::connection, "keep-alive");
req.chunked(true);
req.prepare_payload();
http::write(sock, req);
}
{
http::response<http::vector_body<char> > res;
http::read(sock, buffer, res);
downloads[u] = std::move(res.body());
}
}
for (auto& [url,digits] : downloads) {
auto file = path(url.encoded_resource().decode()).filename();
auto frag = std::min(10ul, digits.size());
std::cout << file << ": " << digits.size() << " digits: " << std::string_view(digits.data(), frag)
<< "..." << std::string_view(digits.data() + digits.size() - frag, frag) << std::endl;
}
}
这会将正常的连接代码(您必须已经拥有)留在单独的函数中
connect
。
当运行时,例如来自 calculat.io 的 PI 数字:
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/url.hpp>
#include <filesystem>
#include <iostream>
#include <map>
namespace asio = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
namespace ssl = asio::ssl;
using asio::ip::tcp;
using boost::url;
using std::filesystem::path;
namespace /*globals*/ {
ssl::context sslctx(ssl::context::sslv23_client);
asio::io_context ctx(1);
auto ex = ctx.get_executor();
// asio::system_executor ex; // use implicit thread pool
using Stream = ssl::stream<tcp::socket>;
struct Connection {
Stream stream;
beast::flat_buffer buffer;
};
std::map<std::string, Connection, std::less<>> connections;
} // namespace
Connection& connect(url u) {
u.normalize();
std::string host = u.host();
std::string service = u.has_port() ? u.port() : "";
if (service.empty()) {
if (u.scheme() == "http") service = "80";
else if (u.scheme() == "https") service = "443";
else service = "80";
}
std::string key = host + ":" + service;
if (auto it = connections.find(key); it != connections.end()) {
std::cout << "Reused connection to " << key << std::endl;
return it->second;
}
std::cout << "New connection to " << key << std::endl;
auto& [k, conn] = *connections.emplace(key, Stream{ex, sslctx}).first;
auto eps = tcp::resolver(ex).resolve(host, service);
if (not SSL_set_tlsext_host_name(conn.stream.native_handle(), host.c_str()))
throw std::runtime_error("SSL_set_tlsext_host_name failed");
tcp::endpoint ep = connect(conn.stream.lowest_layer(), eps);
if (ep.port() != 443)
std::cout << "Warning: Assuming SSL on non-standard port " << ep << std::endl;
conn.stream.set_verify_mode(ssl::verify_none);
conn.stream.handshake(Stream::client);
return conn;
}
int main(int argc, char** argv) {
std::map<url, std::vector<char> > downloads;
for (url u : std::vector<url>(argv + 1, argv + argc)) {
std::cout << "Downloading " << u << std::endl;
auto& [sock, buffer] = connect(u);
// download from the url target using HTTP GET
{
http::request<http::empty_body> req{http::verb::get, u.encoded_resource().decode(), 11};
req.set(http::field::host, u.host());
req.set(http::field::connection, "keep-alive");
req.chunked(true);
req.prepare_payload();
http::write(sock, req);
}
{
http::response<http::vector_body<char> > res;
http::read(sock, buffer, res);
downloads[u] = std::move(res.body());
}
}
for (auto& [url,digits] : downloads) {
auto file = path(url.encoded_resource().decode()).filename();
auto frag = std::min(10ul, digits.size());
std::cout << file << ": " << digits.size() << " digits: " << std::string_view(digits.data(), frag)
<< "..." << std::string_view(digits.data() + digits.size() - frag, frag) << std::endl;
}
}