读取:版本号错误(SSL 例程,ssl3_get_record)
read: wrong version number (SSL routines, ssl3_get_record)
binance-http.hpp
#pragma once
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/json/src.hpp>
#include <boost/json.hpp>
#include <iostream>
#include <string>
#include <exception>
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
namespace ssl = boost::asio::ssl;
using tcp = boost::asio::ip::tcp;
using executor = net::any_io_executor;
using namespace boost::json;
boost::url make_url(boost::url_view base_api, boost::url_view method) {
assert(!method.is_path_absolute());
assert(base_api.data()[base_api.size() - 1] == '/');
boost::urls::error_code ec;
boost::url url;
resolve(base_api, method, url, ec);
if (ec)
throw boost::system::system_error(ec);
return url;
}
void fail_http(beast::error_code ec, char const* what);
namespace binapi{
enum operation {synchronous,asynchronous};
namespace rest{
class httpClient : public std::enable_shared_from_this<httpClient>
{
public:
tcp::resolver resolver_;
beast::ssl_stream<beast::tcp_stream> stream_;
beast::flat_buffer buffer_; // (Must persist between reads)
http::request<http::string_body> req_;
http::response<http::string_body> res_;
std::string API_KEY = "v6uhUtse5Ae1Gyz72eMSbUMGw7VUDdd5AnqobMOW1Llzi4snnfP4YCyY9V74PFJ4";
std::string secret_key = "FW8j4YobD26PVP6QLu0sv4Dv7OzrtfgQKzn8FoIMwGzMW9Y0VmX1DatbLIfXoCHV";
std::string BASE_URL = "https://testnet.binance.vision/api/v3/";
net::io_context ioc;
value json;
public:
httpClient(executor ex, ssl::context& ctx);
ssl::context ctxx{ssl::context::tlsv12_client};
void http_call(boost::url url, http::verb action, operation o);
void on_resolve(beast::error_code ec, tcp::resolver::results_type results);
void on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type);
void on_handshake(beast::error_code ec);
void on_write(beast::error_code ec, std::size_t bytes_transferred);
void on_read(beast::error_code ec, std::size_t bytes_transferred);
void on_shutdown(beast::error_code ec);
void server_time(operation oper);
void avg_price(std::string symbol, operation oper);
};
}
}
namespace binapi
{
namespace rest
{
// Report a failure
void fail_http(beast::error_code ec, char const* what)
{
std::cerr << what << ": " << ec.message() << "\n";
}
httpClient::httpClient(executor ex, ssl::context& ctx)
: resolver_(ex)
, stream_(ex, ctx) {}
// Start the asynchronous operation
void httpClient::http_call(boost::url url, http::verb action, operation o)
{
std::string const host(url.host());
std::string const service = url.has_port() //
? url.port()
: (url.scheme_id() == boost::urls::scheme::https) //
? "https"
: "http";
url.remove_origin(); // becomes req_.target()
// Set SNI Hostname (many hosts need this to handshake successfully)
if(! SSL_set_tlsext_host_name(stream_.native_handle(), host.c_str()))
{
beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()};
std::cerr << ec.message() << "\n";
return;
}
// Set up an HTTP GET/POST/DELETE/PUT request message
// req_.version(version);
req_.method(action);
req_.target(url.c_str());
req_.set(http::field::host, host);
req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req_.set("X-MBX-APIKEY", API_KEY);
//req_.body() = serialize(json::object {{"symbol", "btcusdt"}});
req_.prepare_payload(); // make HTTP 1.1 compliant
if(o==asynchronous){
resolver_.async_resolve(host, service,beast::bind_front_handler(&httpClient::on_resolve,shared_from_this()));
}
else
{
auto const results = resolver_.resolve(host, service);
beast::get_lowest_layer(stream_).connect(results);
// Perform the SSL handshake
stream_.handshake(ssl::stream_base::client);
http::write(stream_, req_);
// Receive the HTTP response
this->buffer_.clear();
this->res_.clear();
http::read(stream_, buffer_, res_);
}
}
void httpClient::on_resolve(beast::error_code ec, tcp::resolver::results_type results)
{
if(ec)
return fail_http(ec, "resolve");
// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(stream_).async_connect(results,beast::bind_front_handler(&httpClient::on_connect,shared_from_this()));
}
void httpClient::on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type)
{
if(ec)
return fail_http(ec, "connect");
// Perform the SSL handshake
stream_.async_handshake(ssl::stream_base::client,beast::bind_front_handler(&httpClient::on_handshake,shared_from_this()));
}
void httpClient::on_handshake(beast::error_code ec)
{
if(ec)
return fail_http(ec, "handshake");
// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
// Send the HTTP request to the remote host
std::cout << "Sending " << req_ << std::endl;
http::async_write(stream_, req_, beast::bind_front_handler(&httpClient::on_write, shared_from_this()));
}
void httpClient::on_write(beast::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if(ec)
return fail_http(ec, "write");
// Receive the HTTP response
this->buffer_.clear();
this->res_.clear();
http::async_read(stream_, buffer_, res_, beast::bind_front_handler(&httpClient::on_read,shared_from_this()));
}
void httpClient::on_read(beast::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if(ec)
return fail_http(ec, "read");
// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
// Gracefully close the stream
stream_.async_shutdown(beast::bind_front_handler(&httpClient::on_shutdown,shared_from_this()));
}
void httpClient::on_shutdown(beast::error_code ec)
{
if(ec == net::error::eof)
{
ec = {};
}
if(ec)
return fail_http(ec, "shutdown");
}
void httpClient::avg_price(std::string symbol,operation oper)
{
this->server_time(operation::synchronous);
static boost::url_view const base_api{"https://testnet.binance.vision/api/v3/"};
boost::url method{"avgPrice"};
method.params().emplace_back("symbol",symbol);
this->http_call(make_url(base_api,method),http::verb::get, oper);
}
}
}
这是我的 main.cpp
样子 :
#include <iostream>
#include <ctime>
#include "boost/url/src.hpp" // can only be included in one source file
#include "binance-ws.hpp"
#include "binance-http.hpp"
using namespace binapi;
int main()
{
net::io_context ioc;
// The SSL context is required, and holds certificates
ssl::context ctx{ssl::context::tlsv12_client};
// Verify the remote server's certificate
ctx.set_verify_mode(ssl::verify_peer);
ctx.set_default_verify_paths();
auto httpclients = std::make_shared<rest::httpClient>(ioc.get_executor(),ctx);
httpclients->avg_price("BTCUSDT",operation::asynchronous);
ioc.run();
}
错误:read: wrong version number (SSL routines, ssl3_get_record)
我认为它来自 async_read(),所以我清除了所有 buffer_
和 res_
但仍然不知道为什么它会再次发生。
我还发现,如果我删除这个:this->server_time()
avg_price() 函数然后它工作正常。
我不知道这里到底发生了什么,请帮助并提前谢谢!
我们无法真正看到,因为代码不是 self-contained。不过,我确实注意到,使用 是 给出的代码,我无法重现该问题。事实上它运行 ubsan/asan 干净。
主要区别在于 this->server_time
被跳过(因为您没有提供它的实现)。所以关于 运行 两个连续请求的问题。
确实,只是重复调用:
this->http_call(make_url(base_api, method), http::verb::get, oper);
this->http_call(make_url(base_api, method), http::verb::get, oper);
确实会导致问题。
然后,当您阅读 http_call
时,问题似乎很明显。
你永远不会关闭流,但是,你第二次使用相同的流,并对其执行 SNI+握手。那是行不通的。
一个简单的 hack 是重置流。但是,如果您计划在每次调用时都进行完全重新连接,为什么甚至 (a) 具有异步模式 (b) 具有 httpClient
class。您可能希望在调用之间保持连接打开,除非发生错误。
binance-http.hpp
#pragma once
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/json/src.hpp>
#include <boost/json.hpp>
#include <iostream>
#include <string>
#include <exception>
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
namespace ssl = boost::asio::ssl;
using tcp = boost::asio::ip::tcp;
using executor = net::any_io_executor;
using namespace boost::json;
boost::url make_url(boost::url_view base_api, boost::url_view method) {
assert(!method.is_path_absolute());
assert(base_api.data()[base_api.size() - 1] == '/');
boost::urls::error_code ec;
boost::url url;
resolve(base_api, method, url, ec);
if (ec)
throw boost::system::system_error(ec);
return url;
}
void fail_http(beast::error_code ec, char const* what);
namespace binapi{
enum operation {synchronous,asynchronous};
namespace rest{
class httpClient : public std::enable_shared_from_this<httpClient>
{
public:
tcp::resolver resolver_;
beast::ssl_stream<beast::tcp_stream> stream_;
beast::flat_buffer buffer_; // (Must persist between reads)
http::request<http::string_body> req_;
http::response<http::string_body> res_;
std::string API_KEY = "v6uhUtse5Ae1Gyz72eMSbUMGw7VUDdd5AnqobMOW1Llzi4snnfP4YCyY9V74PFJ4";
std::string secret_key = "FW8j4YobD26PVP6QLu0sv4Dv7OzrtfgQKzn8FoIMwGzMW9Y0VmX1DatbLIfXoCHV";
std::string BASE_URL = "https://testnet.binance.vision/api/v3/";
net::io_context ioc;
value json;
public:
httpClient(executor ex, ssl::context& ctx);
ssl::context ctxx{ssl::context::tlsv12_client};
void http_call(boost::url url, http::verb action, operation o);
void on_resolve(beast::error_code ec, tcp::resolver::results_type results);
void on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type);
void on_handshake(beast::error_code ec);
void on_write(beast::error_code ec, std::size_t bytes_transferred);
void on_read(beast::error_code ec, std::size_t bytes_transferred);
void on_shutdown(beast::error_code ec);
void server_time(operation oper);
void avg_price(std::string symbol, operation oper);
};
}
}
namespace binapi
{
namespace rest
{
// Report a failure
void fail_http(beast::error_code ec, char const* what)
{
std::cerr << what << ": " << ec.message() << "\n";
}
httpClient::httpClient(executor ex, ssl::context& ctx)
: resolver_(ex)
, stream_(ex, ctx) {}
// Start the asynchronous operation
void httpClient::http_call(boost::url url, http::verb action, operation o)
{
std::string const host(url.host());
std::string const service = url.has_port() //
? url.port()
: (url.scheme_id() == boost::urls::scheme::https) //
? "https"
: "http";
url.remove_origin(); // becomes req_.target()
// Set SNI Hostname (many hosts need this to handshake successfully)
if(! SSL_set_tlsext_host_name(stream_.native_handle(), host.c_str()))
{
beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()};
std::cerr << ec.message() << "\n";
return;
}
// Set up an HTTP GET/POST/DELETE/PUT request message
// req_.version(version);
req_.method(action);
req_.target(url.c_str());
req_.set(http::field::host, host);
req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req_.set("X-MBX-APIKEY", API_KEY);
//req_.body() = serialize(json::object {{"symbol", "btcusdt"}});
req_.prepare_payload(); // make HTTP 1.1 compliant
if(o==asynchronous){
resolver_.async_resolve(host, service,beast::bind_front_handler(&httpClient::on_resolve,shared_from_this()));
}
else
{
auto const results = resolver_.resolve(host, service);
beast::get_lowest_layer(stream_).connect(results);
// Perform the SSL handshake
stream_.handshake(ssl::stream_base::client);
http::write(stream_, req_);
// Receive the HTTP response
this->buffer_.clear();
this->res_.clear();
http::read(stream_, buffer_, res_);
}
}
void httpClient::on_resolve(beast::error_code ec, tcp::resolver::results_type results)
{
if(ec)
return fail_http(ec, "resolve");
// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(stream_).async_connect(results,beast::bind_front_handler(&httpClient::on_connect,shared_from_this()));
}
void httpClient::on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type)
{
if(ec)
return fail_http(ec, "connect");
// Perform the SSL handshake
stream_.async_handshake(ssl::stream_base::client,beast::bind_front_handler(&httpClient::on_handshake,shared_from_this()));
}
void httpClient::on_handshake(beast::error_code ec)
{
if(ec)
return fail_http(ec, "handshake");
// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
// Send the HTTP request to the remote host
std::cout << "Sending " << req_ << std::endl;
http::async_write(stream_, req_, beast::bind_front_handler(&httpClient::on_write, shared_from_this()));
}
void httpClient::on_write(beast::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if(ec)
return fail_http(ec, "write");
// Receive the HTTP response
this->buffer_.clear();
this->res_.clear();
http::async_read(stream_, buffer_, res_, beast::bind_front_handler(&httpClient::on_read,shared_from_this()));
}
void httpClient::on_read(beast::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if(ec)
return fail_http(ec, "read");
// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
// Gracefully close the stream
stream_.async_shutdown(beast::bind_front_handler(&httpClient::on_shutdown,shared_from_this()));
}
void httpClient::on_shutdown(beast::error_code ec)
{
if(ec == net::error::eof)
{
ec = {};
}
if(ec)
return fail_http(ec, "shutdown");
}
void httpClient::avg_price(std::string symbol,operation oper)
{
this->server_time(operation::synchronous);
static boost::url_view const base_api{"https://testnet.binance.vision/api/v3/"};
boost::url method{"avgPrice"};
method.params().emplace_back("symbol",symbol);
this->http_call(make_url(base_api,method),http::verb::get, oper);
}
}
}
这是我的 main.cpp
样子 :
#include <iostream>
#include <ctime>
#include "boost/url/src.hpp" // can only be included in one source file
#include "binance-ws.hpp"
#include "binance-http.hpp"
using namespace binapi;
int main()
{
net::io_context ioc;
// The SSL context is required, and holds certificates
ssl::context ctx{ssl::context::tlsv12_client};
// Verify the remote server's certificate
ctx.set_verify_mode(ssl::verify_peer);
ctx.set_default_verify_paths();
auto httpclients = std::make_shared<rest::httpClient>(ioc.get_executor(),ctx);
httpclients->avg_price("BTCUSDT",operation::asynchronous);
ioc.run();
}
错误:read: wrong version number (SSL routines, ssl3_get_record)
我认为它来自 async_read(),所以我清除了所有 buffer_
和 res_
但仍然不知道为什么它会再次发生。
我还发现,如果我删除这个:this->server_time()
avg_price() 函数然后它工作正常。
我不知道这里到底发生了什么,请帮助并提前谢谢!
我们无法真正看到,因为代码不是 self-contained。不过,我确实注意到,使用 是 给出的代码,我无法重现该问题。事实上它运行 ubsan/asan 干净。
主要区别在于 this->server_time
被跳过(因为您没有提供它的实现)。所以关于 运行 两个连续请求的问题。
确实,只是重复调用:
this->http_call(make_url(base_api, method), http::verb::get, oper);
this->http_call(make_url(base_api, method), http::verb::get, oper);
确实会导致问题。
然后,当您阅读 http_call
时,问题似乎很明显。
你永远不会关闭流,但是,你第二次使用相同的流,并对其执行 SNI+握手。那是行不通的。
一个简单的 hack 是重置流。但是,如果您计划在每次调用时都进行完全重新连接,为什么甚至 (a) 具有异步模式 (b) 具有 httpClient
class。您可能希望在调用之间保持连接打开,除非发生错误。