boost-asio 相关问题

Boost.Asio是一个用于网络和低级I / O编程的跨平台C ++库,它使用现代C ++方法为开发人员提供一致的异步模型。

`boost::asio` - 使特定函数不并行运行

我有一个异步函数,其中包含一个回调函数,可用作协程,与 asio 的示例完全相同: https://github.com/chriskohlhoff/asio/blob/

回答 1 投票 0

从通过 boost::asio::co_spawn 启动的协同例程返回一个值

我有一些asio代码,用于从客户端传输文件。这段代码工作正常,如下: bool FileTransferBehaviour::ConnectAndTransferFile(const std::string&

回答 1 投票 0

从 C++ 中的命名管道同时读写

我有一个进程A正在连续写入命名管道。它已在 O_NONBLOCK | 中打开它。 O_RDWR 模式。 另一个进程 B 正在从该管道中连续读取。它已在 O_NONBLO 中打开它...

回答 1 投票 0

我的服务器中 boost::asio async_connect 的问题是什么?

#包括 #包括 #包括 #包括 #包括 #包括 #include <iostream> #include <string> #include <boost/asio.hpp> #include <boost/asio/thread_pool.hpp> #include <boost/algorithm/string.hpp> #include <boost/bind.hpp> #include "http_server.hh" using namespace boost::asio; using ip::tcp; std::string desired_IP_address = "172.16.0.2"; // For example purposes class Session : public std::enable_shared_from_this<Session> { public: Session(ip::tcp::resolver& resolver,tcp::socket socket, tcp::socket client_socket) : socket_(std::move(socket)), resolver_(resolver), client_socket_(std::move(client_socket)) {} void start() { do_read(); } private: void do_read() { auto self(shared_from_this()); socket_.async_read_some( boost::asio::buffer(data_), [this, self](boost::system::error_code ec, std::size_t length) { if (!ec) { std::string request(data_.data(), length); path_ = extract_path(request); resolver_.async_resolve( ip::tcp::resolver::query("172.16.0.4", "5000"), [this,self](const boost::system::error_code& ec, ip::tcp::resolver::iterator it) { if (ec) { std::cout << "Error resolving " << "localhost" << ": " << ec.message()<< std::endl; return; } // For simplicity, we'll assume the first endpoint will always // be available. //std::cout << "localhost" << ": resolved to " << it->endpoint() // << std::endl; do_connect(it->endpoint()); }); //handle_request_async(path); } }); } void do_connect(const ip::tcp::endpoint& dest) { // Remember that the Asio library will make copies of parameters passed // by const reference, so it's ok to let the endpoint go out of scope // when this method returns. auto self(shared_from_this()); boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("172.16.0.4"), 5000); client_socket_.async_connect(a endpoint, [this, self](const boost::system::error_code& ec) { if (ec) { std::cout << "Error connecting to " << "localhost"<< ": " << ec.message()<< std::endl; return; } //std::cout << "localhost" << ": connected to " // << client_socket_.remote_endpoint() << std::endl; do_send_http_get(); }); } void do_send_http_get() { auto self(shared_from_this()); // At minimum, the remote server needs to know the path being fetched // and the host serving that path. The latter is required because a // single server often hosts multiple domains. request_ = std::string("GET /") + " HTTP/1.1\r\nHost: " + "example.com" + "\r\n\r\n"; async_write( client_socket_, buffer(request_), [this,self](const boost::system::error_code& ec, std::size_t size) { if (ec) { std::cout << "Error sending GET " << ec<< std::endl; return; } //std::cout << "localhost" << ": sent " << size << " bytes"<< std::endl; do_recv_http_get_header(); }); } void do_recv_http_get_header() { // Since HTTP/1.1 is a text based protocol, most of it is human readable // by design. Notice how the "double end of line" character sequence // ("\r\n\r\n") is used to delimit message sections. auto self(shared_from_this()); async_read_until( client_socket_, response_, "\r\n\r\n", [this, self](const boost::system::error_code& ec, std::size_t size) { if (ec) { std::cout << "Error receiving GET header " << ec; return; } //std::cout << "localhost:5000" << ": received " << size << ", streambuf " // << response_.size(); // The asio::streambuf class can use multiple buffers // internally, so we need to use a special iterator to copy out // the header. std::string header( buffers_begin(response_.data()), buffers_begin(response_.data()) + size); response_.consume(size); //std::cout << "----------" << std::endl << "localhost:5000" // << ": header length " << header.size() << std::endl // << header << std::endl; // First we'll check for the explicit "Content-Length" length // field. This provides the exact body length in bytes. size_t pos = header.find("Content-Length: "); if (pos != std::string::npos) { size_t len = std::strtoul( header.c_str() + pos + sizeof("Content-Length: ") - 1, nullptr, 10); do_receive_http_get_body(len - response_.size()); return; } // The other alternative is a chunked transfer. There is a quick // way to determine the remaining length in this case. pos = header.find("Transfer-Encoding: chunked"); if (pos != std::string::npos) { do_receive_http_get_chunked_body(); return; } std::cout << "Unknown body length"; }); } void do_receive_http_get_body(size_t len) { // For "Content-Length" we know exactly how many bytes are left to // receive. auto self(shared_from_this()); async_read( client_socket_, response_, transfer_exactly(len), [this,self] (const boost::system::error_code& ec, std::size_t size) { handle_http_get_body(ec, size); }); } void do_receive_http_get_chunked_body() { // For chunked transfers the final body chunk will be terminated by // another "double end of line" delimiter. auto self(shared_from_this()); async_read_until( client_socket_, response_, "\r\n\r\n", [this,self] (const boost::system::error_code& ec, std::size_t size) { handle_http_get_body(ec, size); }); } void handle_http_get_body(const boost::system::error_code& ec, std::size_t size) { if (ec) { std::cout << "Error receiving GET body " << ec; return; } //std::cout << "localhost:5000" << ": received " << size << ", streambuf " // << response_.size(); // We can finally consume the body and print it out if desired. const auto& data = response_.data(); std::string response_body(buffers_begin(data), buffers_end(data)); response_.consume(size); //std::cout << "----------" << std::endl << "localhost:5000" << ": body length " // << response_body.size() << std::endl; //std::cout << response_body << std::endl; handle_request_async(); } void handle_request_async() { auto self(shared_from_this()); async_response([this,self](const std::string& response) { // std::cout << "path " << path_ << std::endl; // std::cout << response << std::endl; async_write(socket_, boost::asio::buffer(response), [this,self](boost::system::error_code ec, std::size_t /*length*/) { if (!ec) { //std::cout << "response" << std::endl; boost::system::error_code ignored_ec; socket_.shutdown(tcp::socket::shutdown_both, ignored_ec); } }); }); } void async_response(std::function<void(const std::string&)> callback) { // Assuming handle_request returns HTTP_Response asynchronously HTTP_Response* htmlResponse = handle_request(path_); //std::cout << "path: " << path << std::endl; std::string response = htmlResponse->body; //std::cout << "content: " << response << std::endl; callback(response); delete htmlResponse; } // Function to extract path from the HTTP request std::string extract_path(const std::string& request) { // Logic to extract path from the request string // Example logic: extracting the path after the GET method std::string path; //std::cout << request << std::endl; // Implement your path extraction logic here // Find the end of the request line (the first line of the HTTP request) std::size_t requestLineEnd = request.find("\r\n"); if (requestLineEnd != std::string::npos) { std::string requestLine = request.substr(0, requestLineEnd); // Split the request line into parts (method, path, protocol) std::vector<std::string> parts; boost::split(parts, requestLine, boost::is_any_of(" ")); // The second part typically contains the path (e.g., "GET /path HTTP/1.1") if (parts.size() >= 2) { path = parts[1]; // Extract the path from the request line } } return path; } tcp::socket socket_; tcp::socket client_socket_; std::array<char, 8192> data_; std::string path_; std::string request_; boost::asio::streambuf response_; ip::tcp::resolver& resolver_; }; class Server { public: Server(boost::asio::io_context& io_context, short port) : acceptor_(io_context, tcp::endpoint(boost::asio::ip::make_address(desired_IP_address), port)), //acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), socket_(io_context), resolver_(io_context), client_socket_(io_context) { do_accept(); } private: void do_accept() { acceptor_.async_accept( socket_, [this] (boost::system::error_code ec) { if (!ec) { std::make_shared<Session>(resolver_, std::move(socket_), std::move(client_socket_))->start(); } do_accept(); }); } tcp::acceptor acceptor_; tcp::socket socket_; tcp::socket client_socket_; ip::tcp::resolver resolver_; }; int main() { try { boost::asio::io_context io_context; Server server(io_context, 8080); boost::asio::thread_pool pool(40); for (std::size_t i = 0; i < 40; ++i) boost::asio::post(pool, [&io_context]() { io_context.run(); }); pool.join(); } catch (std::exception& e) { std::cerr << "Exception: " << e.what() << std::endl; } return 0; } 我收到错误 “连接到本地主机时出错:无法分配请求的地址” 这是 async_connect 调用时的错误。 当服务器固定到一个核心且该核心的 cpu_utilization 为 100% 时,我会收到此错误。还观察到吞吐量周期性下降。 注意:此服务器与其尝试连接的服务器位于不同的 Linux 网络命名空间中 还有一件事此问题仅在存在网络间命名空间通信时才会发生,而在同一 Linux 网络命名空间或本地主机上运行服务器时不会发生。 io_context.run() 已阻塞。 您将其发布到同样多的服务线程 40 次,具有讽刺意味的是,确保零线程能够取得进一步的进展。 看来您将 asio::threadpool 误认为是 boost::thread_group 之类的东西。相比之下,asio::threadpool已经is-aexecution_context,并且根据定义,所有线程都已经附加到池run服务。 总而言之,更简单的 main 看起来像: int main() try { asio::thread_pool pool; Server server(pool.get_executor(), 8080); pool.join(); } catch (std::exception const& e) { std::cerr << "Exception: " << e.what() << std::endl; } 恐怕代码的其余部分有一些危险信号: async_response 需要 std::function,这有击败 Asio 执行者的风险 你有很多线程。这本身就是一种反模式,但特别是因为没有任何操作使用链 您正在运行定制的 HTTP 消息“解析”。更喜欢使用库(例如 Boost Beast)。具体来说,您的代码看起来不会处理 HTTP 1.1 功能 每个接受动作都来自同一个client_socket_。那是...充其量没用 确实没有任何理由“假设第一个端点有效”。只需使用asio::async_connect 当推送到了紧要关头时,我不清楚您是否只是简单地实现一个中继 HTTP 代理(它看起来很像)还是......一个通用的 HTTP 服务器。现在看起来很像您收到了一个请求,您将其转发到上游。但是,根据给定的代码,来自上游服务器的响应...被视为请求?!?然后将其输入 handle_request。看起来那段代码放错了地方。 无论如何,这是我长时间研究这段代码并提出大量您可能会使用的改进的结果。我用 替换了丢失的标题 http_server.hh using Request = http::request<http::string_body>; using Response = http::response<http::string_body>; http::message_generator handle_request(fs::path path) { std::cout << "path " << path << std::endl; path = fs::current_path() / path; // emulate virtual root, NOTE: not secure! if (exists(path) && fs::is_regular_file(path)) { http::response<http::file_body> res(http::status::ok, 11); error_code ec; res.body().open(path.c_str(), boost::beast::file_mode::scan, ec); std::cout << res.base() << std::endl; return res; } else { http::response<http::string_body> res(http::status::ok, 11); res.body() = "TODO:" + path.native(); std::cout << res << std::endl; return res; } } 这将通过发送该文件来响应与服务器工作目录中现有文件相对应的路径,否则带有请求路径的 TODO: 正文。 住在Coliru #include <boost/asio.hpp> #include <boost/beast.hpp> #include <filesystem> #include <iostream> namespace asio = boost::asio; namespace http = boost::beast::http; namespace fs = std::filesystem; using asio::ip::tcp; using boost::system::error_code; // #include "http_server.hh" using Request = http::request<http::string_body>; using Response = http::response<http::string_body>; http::message_generator handle_request(fs::path path) { std::cout << "path " << path << std::endl; path = fs::current_path() / path; // emulate virtual root, NOTE: not secure! if (exists(path) && fs::is_regular_file(path)) { http::response<http::file_body> res(http::status::ok, 11); error_code ec; res.body().open(path.c_str(), boost::beast::file_mode::scan, ec); std::cout << res.base() << std::endl; return res; } else { http::response<http::string_body> res(http::status::ok, 11); res.body() = "TODO:" + path.native(); std::cout << res << std::endl; return res; } } struct Session : std::enable_shared_from_this<Session> { Session(tcp::socket socket) : downstream_{std::move(socket)} , upstream_{downstream_.get_executor()} , resolver_{downstream_.get_executor()} {} ~Session() { std::cerr << "Session closed" << std::endl; } void start() { std::cerr << "Accepted from " << downstream_.remote_endpoint() << std::endl; do_read(); } private: void do_read() { http::async_read( downstream_, downbuf_, request_, [this, self = shared_from_this()](error_code ec, size_t /*length*/) { std::cout << "Received " << ec.message() << ": " << request_.base() << std::endl; if (ec) return; path_ = request_.target(); auto host = request_.at(http::field::host); auto port = "http"; // TODO parse port from host? resolver_.async_resolve(host, port, [this, self](error_code ec, auto eps) { std::cout << "Resolved upstream: " << ec.message() << std::endl; if (ec) return; for (auto&& ep : eps) std::cout << " - candidate: " << ep.endpoint() << std::endl; asio::async_connect(upstream_, eps, [this, self](error_code ec, tcp::endpoint) { std::cout << "Connect upstream: " << ec.message() << std::endl; if (!ec) { std::cout << "upstream: " << upstream_.remote_endpoint() << std::endl; do_relay(); } }); }); }); } void do_relay() { #if true // keep the request as received from downstream_ #else // At minimum, server needs the path & host header request_ = Request{http::verb::get, "/", 11}; request_.set(http::field::host, "example.com"); #endif async_write(upstream_, request_, [this, self = shared_from_this()](error_code ec, size_t size) { std::cout << "Relayed upstream: " << ec.message() << std::endl; if (!ec) { std::cout << "sent to upstream: " << size << " bytes" << std::endl; do_recv_response(); } }); } void do_recv_response() { response_.clear(); http::async_read( upstream_, upbuf_, response_, [this, self = shared_from_this()](error_code ec, size_t /*size*/) { if (ec) { std::cout << "Error receiving GET header " << ec.message(); return; } std::cout << "-- Response headers -- " << response_.base() << std::endl; if (response_.has_content_length()) { std::cout << "-- Content-Length header = " << response_.at(http::field::content_length) << std::endl; } if (response_.chunked()) std::cout << "-- Received using chunked encoding" << std::endl; std::cout << "-- Actual Body Length: " << response_.body().length() << std::endl; std::cout << "-- Body: " << response_.body() << std::endl; // this looks REALY out of place boost::beast::async_write( // downstream_, handle_request(path_), [this, self = shared_from_this()](error_code ec, size_t /*length*/) { if (!ec) { // std::cout << "response" << std::endl; error_code ignored_ec; downstream_.shutdown(tcp::socket::shutdown_both, ignored_ec); } }); }); } tcp::socket downstream_; asio::streambuf downbuf_; tcp::socket upstream_; asio::streambuf upbuf_; Request request_; Response response_; std::string path_; tcp::resolver resolver_; }; struct Server { Server(asio::any_io_executor ex, uint16_t port, std::string bind_address = "172.16.0.2") : acceptor_(ex, tcp::endpoint(asio::ip::make_address(bind_address), port)) { do_accept(); } private: void do_accept() { acceptor_.async_accept(make_strand(acceptor_.get_executor()), [this](error_code ec, tcp::socket s) { if (!ec) std::make_shared<Session>(std::move(s))->start(); do_accept(); }); } tcp::acceptor acceptor_; }; int main() try { asio::thread_pool pool; Server server(pool.get_executor(), 8080, "0.0.0.0"); pool.join(); } catch (std::exception const& e) { std::cerr << "Exception: " << e.what() << std::endl; } 带有本地互动演示:

回答 1 投票 0

用于在本地主机上运行 boost asio 服务器的网络设置

我写了一个客户端client.cpp和一个服务器server.cpp,它们编译得很好。但我不知道如何将 client.cpp 连接到 localhost 中的 server.cpp 我用 Boost.Asio 编写了代码,它们编译...

回答 1 投票 0

接收客户端发送的数据时发生错误。 :remote_endpoint:传输端点未连接

我使用Boost.asio创建了一个使用tcp方法进行通信的服务器和客户端。服务器从个人或少数客户端接收信息没有问题,但如果...

回答 1 投票 0

使用 Boost.Asio 的 C++ 命名管道服务器

我一直直接使用Windows API来创建命名管道服务器,如下所示: #包括 #包括 int main() { // 定义命名管道的名称 合作...

回答 1 投票 0

boost::asio::steady_timer的最大等待时间

如何设置boost::asio::steady_timer的最大等待时间(以毫秒为单位)? 当我设置 boost::asio::chrono::milliseconds::max() 时,计时器立即返回(为什么?)。 当我设置得比较短时...

回答 1 投票 0

调用async_resolve.cancel(),但async_resolve的回调处理程序不返回boost::asio::error::operation_aborted

我使用boost asio来处理http请求和应答,为了避免async_resolve不调用它的回调处理程序,我设置了一个超时,就像这样: 无效解析() { solver_.async_resolve(查询,str...

回答 3 投票 0

Linux boost::asio::spawn 中无法完成连接

如果服务器启动后无法连接,为什么? boost_1_73_0、Linux操作系统 #include“boost/asio.hpp” #include“boost/asio/spawn.hpp” int main() { ...

回答 1 投票 0

如何用 boost::asio 等待`std::future`?

我有一个外部库的异步操作,我无法更改该操作,但我可以对其进行回调。我想等待它完成但不阻塞 io_context。 就像是: // 例如...

回答 1 投票 0

使用 boost::asio libary 进行文件传输

可悲的是,当我尝试将文件从 nfoserver 传输到我的 PC 时,读取的文件大小不正确并且永远不会结束 do while 循环。任何帮助将不胜感激!当文件大小正确时它会停止...

回答 1 投票 0

Boost ASIO async_read_until 不连续读取消息的问题

我正在使用 boost.asio 实现一个网络库。这是 TCP 类的读取函数。 我的一个测试用例有一个客户端连接到服务器,一旦握手完成,服务器

回答 1 投票 0

Boost.asio 使用协程实现多个连接的 echo 服务器

我修改了boost.asio官方网页上的一个unix socket echo服务器示例,并采用它来使用协程。 我想允许该服务器同时处理多个连接。 一种解决方案...

回答 1 投票 0

TCP 代理 - 在收到客户端断开连接时关闭与服务器的连接

在我的代理代码中,当从客户端(代理充当服务器)收到断开连接信号时,它无法正确终止服务器端的关联会话(其中...

回答 1 投票 0

boost TCP 服务器在 Linux 下无法正确接受连接

我正在开发客户端/服务器 TCP 应用程序。它在 Windows 下工作得很好,但我在 Linux 下遇到了问题:当建立新连接时,服务器没有收到通知...

回答 1 投票 0

使用 boost::beast 发送 http 请求时可能提高性能

我正在构建一个低延迟项目,尽可能快地发送http请求是一个关键组件。这是我当前的 http 请求的构造 #包括 #包括 我正在构建一个低延迟项目,尽快发送 http 请求是一个关键组件。这是我当前 http 请求的构造 #include <thread> #include <iostream> #include <coroutine> #include <optional> #include <variant> #include <vector> #include <utility> #include <string> #include <chrono> #include <boost/asio.hpp> #include <boost/asio/ssl.hpp> #include <boost/json.hpp> #include <boost/beast.hpp> #include <boost/asio/io_context.hpp> #include <boost/asio/ssl/context.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/spawn.hpp> #include <boost/coroutine/all.hpp> #include <boost/beast/ssl/ssl_stream.hpp> #include <boost/beast/core/tcp_stream.hpp> #include <boost/beast/core/flat_static_buffer.hpp> #include <boost/beast/http.hpp> #include <boost/beast/http/string_body.hpp> #include <boost/beast/http/verb.hpp> #include <boost/asio/awaitable.hpp> #include <boost/asio/detached.hpp> class http_client { private: using response = boost::beast::http::response<boost::beast::http::string_body>; std::string http_hostname; std::string ip_address; boost::asio::ssl::context ssl_context; boost::asio::ip::tcp::resolver hostname_resolver; std::optional<boost::beast::ssl_stream<boost::beast::tcp_stream>> tcp_stream; boost::beast::flat_static_buffer<4 * 1024 * 1024> receive_flat_buffer; public: http_client(const std::string& http_name, boost::asio::io_context& io_context) : http_hostname(http_name), ssl_context(boost::asio::ssl::context::tlsv12_client), hostname_resolver(io_context), tcp_stream(boost::beast::ssl_stream<boost::beast::tcp_stream>(io_context, ssl_context)), receive_flat_buffer() { ssl_context.set_verify_mode(boost::asio::ssl::verify_peer); ssl_context.set_options( boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 | boost::asio::ssl::context::single_dh_use); if (!SSL_set_tlsext_host_name(tcp_stream->native_handle(), http_hostname.c_str())) { boost::beast::error_code error_code{static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()}; throw boost::beast::system_error{error_code}; } auto const resolved_endpoint = hostname_resolver.resolve(http_hostname, "443"); ip_address = resolved_endpoint->endpoint().address().to_string(); boost::beast::get_lowest_layer(tcp_stream.value()).connect(resolved_endpoint); boost::beast::get_lowest_layer(tcp_stream.value()).socket().set_option(boost::asio::socket_base::keep_alive(true)); boost::beast::get_lowest_layer(tcp_stream.value()).socket().set_option(boost::asio::ip::tcp::no_delay(true)); std::cout << "Connected to REST endpoint at IP address <" << ip_address << "> which was resolved from <" << resolved_endpoint->host_name() << std::endl; tcp_stream->handshake(boost::asio::ssl::stream_base::client); } void send_request(boost::asio::io_context& io_context, const std::string& target, const std::function<void(response)>& callback) { boost::asio::spawn( io_context, [target = std::move(target), callback = std::move(callback), this](boost::asio::yield_context yield_context) mutable { boost::beast::http::request<boost::beast::http::string_body> http_request{ boost::beast::http::verb::get, target, 11}; http_request.set(boost::beast::http::field::host, http_hostname); http_request.set(boost::beast::http::field::content_type, "application/json"); http_request.set(boost::beast::http::field::connection, "Keep-Alive"); http_request.set(boost::beast::http::field::keep_alive, "timeout=86400"); http_request.keep_alive(true); http_request.prepare_payload(); size_t bytes_transferred = boost::beast::http::async_write(tcp_stream.value(), http_request, yield_context); response http_response; boost::beast::http::async_read(tcp_stream.value(), receive_flat_buffer, http_response, yield_context); callback(http_response); } ); } }; int main() { boost::asio::io_context io_context{}; std::string host_name{"fapi.binance.com"}; http_client client(host_name, io_context); for (int i = 0; i < 100; i++) { auto const stime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); client.send_request(io_context, "/fapi/v1/time", [&](boost::beast::http::response<boost::beast::http::string_body> const& http_response) {}); auto const etime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout << "time diff = " << etime - stime << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } io_context.run(); return 0; } 这是我正在使用的编译器标志 g++ -std=c++20 -O2 -flto -g beast_http_client.cpp -I/home/dev/vcpkg/installed/x64-linux/include -L/home/dev/vcpkg/installed/x64-linux/lib -lboost_system -lboost_coroutine -lboost_thread -lboost_json -lssl -lcrypto -lboost_context 我对此进行了计时,延迟平均约为 10-20 我们。我想知道是否可以做任何改进来将其降低到低个位数微。我知道 boost::beast 是一个相当重的库,但是是的,我想我会向专家学习有关明显优化的知识 硬件:我在具有 Intel Xeon 处理器 3GHz 的 AWS 虚拟机上运行此程序 有很多问题。 就像我在评论中提到的那样,不清楚您要测量什么(并以某种方式称为“延迟”)。 但是继续阅读,就会发现代码在很多方面都被破坏了。 main循环调度 100 个协程,每个协程之间等待 100 毫秒(完全没有原因),然后,仅在 main 末尾通过调用 io_context.run() 一次性执行所有协程。 这不仅会在服务器上造成类似于拒绝服务攻击的情况,而且还明显违反了一次只能执行一个写入操作的限制(这尤其适用于 SSL 流,也适用于 SSL 流)底层 POSIX 互联网域流套接字)。 事实上,如果您运行调试构建,毫无疑问会有像这样的断言为您中止程序: 最测试: /home/sehe/custom/superboost/boost/beast/core/detail/stream_base.hpp:116: void boost::beast::detail::stream_base::pending_guard::assign(bool&): Assertion `! *b_' failed. 其他一些问题包括: 解决这些问题和一些可靠性问题: 你经常使用const变量move。不是很有用 你多余地传递了io_context引用而不是使用执行器 您可能会晚设置套接字选项 您无缘无故地使用optional<>。如果必须的话,至少使用 stream_(std::in_place, ...) 来构建 std::function 引入类型擦除(虚拟调度)和复制 您可能想要计算开始-完成(往返)时间 您在 GET 请求中使用 string_body 而不是 empty_body 住在Coliru #include <iomanip> #include <iostream> #include <boost/asio.hpp> #include <boost/asio/spawn.hpp> #include <boost/asio/ssl.hpp> #include <boost/beast.hpp> #include <boost/beast/ssl.hpp> namespace net = boost::asio; namespace ssl = net::ssl; namespace beast = boost::beast; namespace http = beast::http; using beast::error_code; using net::ip::tcp; using namespace std::chrono_literals; static constexpr auto now = std::chrono::steady_clock::now; using duration = std::chrono::steady_clock::duration; class http_client { private: using Stream = beast::ssl_stream<beast::tcp_stream>; std::string host_; ssl::context ctx_{ssl::context::tlsv12_client}; Stream stream_; std::string ip_address; beast::flat_static_buffer<4 << 10> buf_; public: using response = http::response<http::string_body>; http_client(std::string host, net::io_context& ioc) : host_(std::move(host)), stream_(ioc, ctx_) { ctx_.set_verify_mode(ssl::verify_peer); using C = ssl::context; ctx_.set_options(C::default_workarounds | C::no_sslv2 | C::no_sslv3 | C::single_dh_use); if (!SSL_set_tlsext_host_name(stream_.native_handle(), host_.c_str())) throw beast::system_error(::ERR_get_error(), net::error::get_ssl_category()); auto eps = tcp::resolver(ioc).resolve(host_, "443"); ip_address = eps->endpoint().address().to_string(); { auto& ll = beast::get_lowest_layer(stream_); auto& s = ll.socket(); s.open(tcp::v4()); s.set_option(tcp::no_delay(true)); s.set_option(tcp::socket::keep_alive(true)); ll.connect(eps); } std::cout << "Connected to REST endpoint at IP address " << quoted(ip_address) << " which was resolved from " << quoted(eps->host_name()) << std::endl; stream_.handshake(Stream::client); } // template <typename F> // requires std::invocable<F, error_code, response, duration> void send_request(std::string target, std::function<void(error_code, response, duration)> callback) { spawn(stream_.get_executor(), [start = now(), target = std::move(target), cb = std::move(callback), this](net::yield_context yield) mutable { http::request<http::empty_body> http_request{http::verb::get, target, 11}; http_request.set(http::field::host, host_); http_request.set(http::field::content_type, "application/json"); http_request.set(http::field::connection, "Keep-Alive"); http_request.set(http::field::keep_alive, "timeout=86400"); http_request.keep_alive(true); http_request.prepare_payload(); /*size_t bytes_transferred =*/async_write(stream_, http_request, yield); response http_response; error_code ec; async_read(stream_, buf_, http_response, yield[ec]); std::move(cb)(ec, std::move(http_response), now() - start); }); } }; void send_loop(http_client& client, std::string const& target, unsigned n) { if (n == 0) return; client.send_request(target, [=, &client](error_code ec, http_client::response res, duration dur) { send_loop(client, target, n - 1); // only now it is safe to schedule a new write std::cout << "#" << n << " " << ec.message() << " in " << (dur / 1ms) << " ms"; if (!ec) std::cout << " HTTP " << res.reason(); std::cout << std::endl; }); } int main() { net::io_context io_context; http_client client("fapi.binance.com", io_context); send_loop(client, "/fapi/v1/time", 3); io_context.run(); } 本地演示: 其他问题/改进 您依赖永不失败的连接 常见的 HTTP 服务器实现限制了管道请求的数量 您正在使用默认类型的已擦除执行器(any_io_executor) 你正在使用堆栈协程,这有点重 您可能应该考虑一个单独的 IO 线程和可能的多个连接。参见例如如何使此 HTTPS 连接在 Beast 中持久存在? 从这里获取想法。

回答 1 投票 0

asio(独立,非升压)async_write 处理程序未由 io_context.run() 或 io_context.poll() 调用

(我知道标签是boost-asio,但我使用的是独立的asio) 我正在调试我的 tcp 客户端应用程序,它可以归结为: // 检查点 1 asio::async_write(套接字,asio::buffer(输入),[](...

回答 1 投票 0

Boost ASIO SSL 握手失败

尝试使用 Boost ASIO 安全连接到远程 IMAP 服务器时,每次连接时服务器握手都会失败。异常消息如下: 握手:未注册方案(STORE rout...

回答 3 投票 0

在 macOS 中使用 boost.asio 协程会触发大量文件描述符

我有一个程序,使用 async_read 和来自 unix 域套接字的收益。 void readSocket(const std::list>::iterator &socket, 升压::asio::

回答 1 投票 0

© www.soinside.com 2019 - 2024. All rights reserved.