繁体   English   中英

WebSocket 握手被远程对端拒绝

[英]WebSocket handshake declined by remote peer

所以我一直在尝试遵循 ssl websocket 连接到 binance 的提升代码,但在尝试启动握手时我一直收到错误消息。 我已经删除了 load_root_certificates function 顺便说一句,因为它说 function 是未定义的,我看到一个帖子说你不需要,因为它会在我的机器中加载默认证书。 不确定最后一个陈述是否属实。

#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/beast/core/ostream.hpp>
#include <string>
#include <utility>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <iostream>
#include "JSONParser.hpp"
#include "data.hpp"


#define ONEHOUR_ONEMONTH 672
#define ONEMIN_ONEWEEK 10080
#define ONESEC_ONEDAY 86400

std::string create_subscription_message() {
    boost::property_tree::ptree message;
    message.put("method", "SUBSCRIBE");

    std::vector<std::string> streams = {"btcusdt@kline_1m"};
    boost::property_tree::ptree params;
    for(auto& stream: streams)
        params.push_back(std::make_pair("", boost::property_tree::ptree(stream)));

    message.add_child("params", params);
    message.put("id", 1);

    std::stringstream ss;
    boost::property_tree::write_json(ss, message);
    return ss.str();
}

namespace beast = boost::beast;         // from <boost/beast.hpp>
namespace http = beast::http;           // from <boost/beast/http.hpp>
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
namespace net = boost::asio;            // from <boost/asio.hpp>
namespace ssl = boost::asio::ssl;       // from <boost/asio/ssl.hpp>
using tcp = boost::asio::ip::tcp; 

int main() {
    try {
        cData candlesticks(ONEMIN_ONEWEEK);
        std::string s = "{\n  \"e\": \"kline\",\n  \"E\": 123456789,\n  \"s\": \"BNBBTC\",\n  \"k\": {\n    \"t\": 123400000,\n    \"T\": 123460000,\n    \"s\": \"BNBBTC\",\n    \"i\": \"1m\",\n    \"f\": 100,\n    \"L\": 200,\n    \"o\": \"0.0010\",\n    \"c\": \"0.0020\",\n    \"h\": \"0.0025\",\n    \"l\": \"0.0015\",\n    \"v\": \"1000\",\n    \"n\": 100,\n    \"x\": false,\n    \"q\": \"1.0000\",\n    \"V\": \"500\",\n    \"Q\": \"0.500\",\n    \"B\": \"123456\"\n  }\n}";
        candlesticks.addCandlestick(s);
        candlesticks.printCandlestick(candlesticks.accessDataAtIndex(0));
        // WebSocket endpoint
        std::string host = "wss://stream.binance.com";
        std::string port = "443";
        // Create the I/O context
        boost::asio::io_context ioc;

        // Creates SSL context and holds certificate
         ssl::context ctx{ssl::context::tlsv12_client};

    
        tcp::resolver resolver(ioc);
        // Create the WebSocket stream
        websocket::stream<beast::ssl_stream<tcp::socket>> ws{ioc, ctx};

        // Resolve the hostname
        auto endpoints = resolver.resolve(host, port);

        // Connect to the first endpoint in the list
        auto ep = net::connect(get_lowest_layer(ws), endpoints);
        
        if(! SSL_set_tlsext_host_name(ws.next_layer().native_handle(), host.c_str()))
            throw beast::system_error(
                beast::error_code(
                    static_cast<int>(::ERR_get_error()),
                    net::error::get_ssl_category()),
                "Failed to set SNI Hostname");
        host += ':' + std::to_string(ep.port());

        ws.next_layer().handshake(ssl::stream_base::client);
        
        ws.set_option(websocket::stream_base::decorator(
            [](websocket::request_type& req)
            {
                req.set(http::field::user_agent,
                    std::string(BOOST_BEAST_VERSION_STRING) +
                        " websocket-client-coro");
            }));

        boost::beast::error_code ec;

        ws.handshake(host, "/ws/ethusdt@kline_5m", ec);

        if(ec) {
            std::cerr << "Error: " << ec.message() << std::endl;
            return EXIT_FAILURE;
        }
        //subscription message
        std::string subscription_message = create_subscription_message();

        // Send the subscription message
        ws.write(boost::asio::buffer(subscription_message));

        // Receive messages
        for (;;) {
            boost::beast::multi_buffer buffer;
            ws.read(buffer);
            std::cout << boost::beast::make_printable(buffer.data()) << std::endl;
            if (buffer.size() == 0) {
                break;
            }
            auto message = boost::beast::buffers_to_string(buffer.data());
            if (message == "ping") {
                buffer.consume(buffer.size());
                ws.write(boost::asio::buffer("pong"));
            }
        }

    }
    catch (std::exception const& e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    
}

错误:WebSocket 握手被远程对等方拒绝

整个问题似乎是主机格式错误:

std::string host = "wss://stream.binance.com";

那是 url,不是 FQDN 或有效的 IP 地址。 其余部分采用 FQDN(因为它用于 TCP 地址解析、SNI 和 HTTP 主机标头)。 相反,我期望:

std::string host = "stream.binance.com";

事实上,随着这种变化,错误从

Error: resolve: Host not found (authoritative) [asio.netdb:1 at /home/sehe/custom/superboost/boost/asio/detail/resolver_service.hpp:84:5 in function 'boost::asio::detail::resolver_service<Protocol>::results_type boost::asio::detail::resolver_service<Protocol>::resolve(implementation_type&, const query_type&, boost::system::error_code&)']

进入

{"error":{"code":2,"msg":"Invalid request: request ID must be an unsigned integer"}}
Error: stream truncated [asio.ssl.stream:1]

这很好,因为它意味着连接被接受,只有你的请求是无效的。 这是你接下来要看的东西。

测试代码

自包含为: Coliru

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>

#include <iomanip>
#include <iostream>

#define ONEHOUR_ONEMONTH 672
#define ONEMIN_ONEWEEK 10080
#define ONESEC_ONEDAY 86400

struct cData {
    //unsigned ticks;
    std::vector<std::string> what_ever;

    cData(unsigned /*t*/) //: ticks(t)
    {}

    void        addCandlestick(std::string_view cs) { what_ever.emplace_back(cs); }
    std::string accessDataAtIndex(size_t idx) const { return what_ever.at(idx); }
    void        printCandlestick(std::string_view cs) const {
        std::cout << "printCandlestick: " << quoted(cs) << std::endl;
    }
};

std::string create_subscription_message() {
    boost::property_tree::ptree message;
    message.put("method", "SUBSCRIBE");

    std::vector<std::string>    streams = {"btcusdt@kline_1m"};
    boost::property_tree::ptree params;
    for (auto& stream : streams)
        params.push_back(std::make_pair("", boost::property_tree::ptree(stream)));

    message.add_child("params", params);
    message.put("id", 1);

    std::stringstream ss;
    boost::property_tree::write_json(ss, message);
    return ss.str();
}

namespace beast     = boost::beast;     // from <boost/beast.hpp>
namespace http      = beast::http;      // from <boost/beast/http.hpp>
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
namespace net       = boost::asio;      // from <boost/asio.hpp>
namespace ssl       = boost::asio::ssl; // from <boost/asio/ssl.hpp>
using tcp           = boost::asio::ip::tcp;

int main() {
    try {
        cData       candlesticks(ONEMIN_ONEWEEK);
        std::string s =
            R"({
  "e": "kline",
  "E": 123456789,
  "s": "BNBBTC",
  "k": {
    "t": 123400000,
    "T": 123460000,
    "s": "BNBBTC",
    "i": "1m",
    "f": 100,
    "L": 200,
    "o": "0.0010",
    "c": "0.0020",
    "h": "0.0025",
    "l": "0.0015",
    "v": "1000",
    "n": 100,
    "x": false,
    "q": "1.0000",
    "V": "500",
    "Q": "0.500",
    "B": "123456"
  }
})";
        candlesticks.addCandlestick(s);
        candlesticks.printCandlestick(candlesticks.accessDataAtIndex(0));

        // WebSocket endpoint
        std::string host = "stream.binance.com";
        std::string port = "443";

        boost::asio::io_context ioc;

        ssl::context ctx{ssl::context::tlsv12_client};

        tcp::resolver resolver(ioc);
        websocket::stream<beast::ssl_stream<tcp::socket>> ws{ioc, ctx};

        // Resolve the hostname
        auto endpoints = resolver.resolve(host, port);

        // Connect to the first endpoint in the list
        auto ep = net::connect(get_lowest_layer(ws), endpoints);

        if (!SSL_set_tlsext_host_name(ws.next_layer().native_handle(), host.c_str()))
            throw beast::system_error(
                beast::error_code(static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()),
                "Failed to set SNI Hostname");
        host += ':' + std::to_string(ep.port());

        ws.next_layer().handshake(ssl::stream_base::client);

        ws.set_option(websocket::stream_base::decorator([](websocket::request_type& req) {
            req.set(http::field::user_agent,
                    std::string(BOOST_BEAST_VERSION_STRING) + " websocket-client-coro");
        }));

        boost::beast::error_code ec;

        ws.handshake(host, "/ws/ethusdt@kline_5m", ec);

        if (ec) {
            std::cerr << "Error: " << ec.message() << std::endl;
            return EXIT_FAILURE;
        }
        // subscription message
        std::string subscription_message = create_subscription_message();

        // Send the subscription message
        ws.write(boost::asio::buffer(subscription_message));

        // Receive messages
        for (;;) {
            boost::beast::multi_buffer buffer;
            ws.read(buffer);
            std::cout << boost::beast::make_printable(buffer.data()) << std::endl;
            if (buffer.size() == 0) {
                break;
            }
            auto message = boost::beast::buffers_to_string(buffer.data());
            if (message == "ping") {
                buffer.consume(buffer.size());
                ws.write(boost::asio::buffer("pong"));
            }
        }

    } catch (std::exception const& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
}

本地 output:

在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM