boost::asio 从 ssl 流读取所有数据(不使用 EOF 和连接:关闭标头)

问题描述 投票:0回答:1

我正在开发一个程序,需要从

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

asynchronous boost https tcp asio
1个回答
0
投票

您必须实现 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 数字:

enter image description here

完整列表

住在科里鲁

#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;
    }   
}
© www.soinside.com 2019 - 2024. All rights reserved.