使用Boost.Asio发起HTTPS GET请求时握手失败问题排查
问题分析与解决
核心错误原因
你遇到的握手错误packet length too long,根源是连接了错误的端口:代码中指定的端口是80,这是HTTP协议的默认端口,而HTTPS请求必须使用443端口。当你向80端口发送SSL握手请求时,服务器返回的是HTTP响应内容,SSL解析器无法识别,从而抛出数据包长度异常。
另外你怀疑的手动导入证书问题,Boost.Asio其实可以像curl一样自动加载系统根证书,不需要手动下载添加——ctx.set_default_verify_paths()调用已经会自动加载操作系统内置的信任根证书列表,和curl的行为一致。
修正后的代码
#include <boost/beast/core.hpp> #include <boost/beast/http.hpp> #include <boost/beast/version.hpp> #include <boost/asio/connect.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/ssl/error.hpp> #include <boost/asio/ssl/stream.hpp> #include <cstdlib> #include <iostream> #include <string> namespace http = boost::beast::http; using tcp = boost::asio::ip::tcp; namespace ssl = boost::asio::ssl; int main() { try { const std::string host = "api.binance.com"; const std::string port = "443"; // 修正为HTTPS标准端口 const std::string target = "/api/v3/time"; boost::asio::io_context ioc; // 使用现代TLS客户端上下文,替代老旧的sslv23_client ssl::context ctx{ssl::context::tls_client}; // 自动加载系统根证书,无需手动导入 ctx.set_default_verify_paths(); // 显式启用证书验证(默认已开启,写出更清晰) ctx.set_verify_mode(ssl::verify_peer); tcp::resolver resolver{ioc}; ssl::stream<tcp::socket> stream{ioc, ctx}; // 设置SNI主机名,现代HTTPS服务器要求的必要步骤 if(!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) { boost::system::error_code ec{static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()}; throw boost::system::system_error{ec}; } const auto result = resolver.resolve(host, port); boost::asio::connect(stream.next_layer(), result.begin(), result.end()); stream.handshake(ssl::stream_base::client); http::request<http::string_body> req{http::verb::get, target, 11}; // 使用HTTP/1.1 req.set(http::field::host, host); req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); http::write(stream, req); boost::beast::flat_buffer buffer; http::response<http::dynamic_body> res; http::read(stream, buffer, res); std::cout << res << std::endl; boost::system::error_code ec; stream.shutdown(ec); // 忽略正常的EOF和未连接错误 if (ec && ec != boost::asio::error::eof && ec != boost::system::errc::not_connected) { throw boost::system::system_error{ec}; } } catch(const std::exception& e) { std::cout << "Error: " << e.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; }
关键改动说明
- 端口修正:将
port从80改为443,适配HTTPS协议的标准端口。 - SSL上下文优化:使用
tls_client替代老旧的sslv23_client,兼容现代TLS协议版本。 - 移除手动证书加载:依赖
set_default_verify_paths()自动加载系统根证书,和curl行为完全一致。 - 添加SNI设置:调用
SSL_set_tlsext_host_name设置服务器名称指示,确保多域名服务器能返回正确的证书。 - HTTP版本更新:将请求版本从
10(HTTP/1.0)改为11(HTTP/1.1),符合现代服务器规范。
验证效果
编译运行修正后的代码,会得到和curl一致的返回结果:
{"serverTime":1679237703479}
内容的提问来源于stack exchange,提问作者Freddy Martinez Garcia
相关产品推荐
相关产品推荐

