0

我正在尝试连接到 Kraken websocket API。但我收到“WebSocket 握手被远程对等方拒绝”错误。

我已经为 websocket 和交易所的 rest api 客户端编写了一个包装类。它与 Binance websocket API 配合得很好,但是 Kraken websocket 连接不成功。

我也尝试过不同类型的 tls(ssl::context ctx_webSocket{ ssl::context::tlsv13_client };) 版本,但结果是一样的。

    class Exchange {
public:
    Exchange(std::string name, const std::string& http_host) :m_name(std::move(name)) {
        init_http(http_host);
    }


    void init_http(std::string const& host) {

        const auto results{ resolver.resolve(host,"443") };
        get_lowest_layer(stream).connect(results);
        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 };
        }
        stream.handshake(ssl::stream_base::client);
    }

    void init_webSocket(std::string const& host, std::string const& port, std::string const& target) {
        auto const results = resolver_webSocket.resolve(host, port);
        net::connect(ws.next_layer().next_layer(), results.begin(), results.end());
        ws.next_layer().handshake(ssl::stream_base::client);
        // Set a decorator to change the User-Agent of the handshake
        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");
        }));
        ws.handshake(host, target.c_str());
    }

    void read_Socket() {
        ws.read(buffer);

    }
    void write_Socket(const std::string& text) {
        ws.write(net::buffer(text));
    }

    std::string get_socket_data() {
        return beast::buffers_to_string(buffer.data());
    }
    void buffer_clear() {
        buffer.clear();

    }
    void webSocket_close() {
        ws.close(websocket::close_code::none);
    }
private:
    // HTTP REQUEST SET //
    std::string m_name;
    net::io_context ioc;
    ssl::context ctx{ ssl::context::tlsv12_client };
    tcp::resolver resolver{ ioc };
    Stream stream{ ioc, ctx };

    // WEB SOCKET SET //
    std::string m_web_socket_host;
    std::string m_web_socket_port;
    beast::flat_buffer buffer;
    net::io_context ioc_webSocket;
    ssl::context ctx_webSocket{ ssl::context::tlsv13_client };
    tcp::resolver resolver_webSocket{ ioc_webSocket };
    websocket::stream<beast::ssl_stream<tcp::socket>> ws{ ioc_webSocket, ctx_webSocket };

};

int main()
{
    Exchange kraken{ "kraken","api.kraken.com" };

    try
    {

        kraken.init_webSocket("ws.kraken.com", "443", "/");

        while (true)
        {
            kraken.read_Socket();
            std::cout << kraken.get_socket_data();

            return 1;


            kraken.buffer_clear();
        }
        kraken.webSocket_close();
    }


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

}
4

2 回答 2

1

解决了。我已经发现问题出在哪里了。在 websocket 握手之前,必须设置 SNI 主机名,下面是工作测试代码。

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <iostream>

namespace net       = boost::asio;
namespace ssl       = net::ssl;
namespace beast     = boost::beast;
namespace http      = beast::http;
namespace websocket = beast::websocket;

using tcp      = net::ip::tcp;
using Request  = http::request<http::string_body>;
using Stream   = beast::ssl_stream<beast::tcp_stream>;
using Response = http::response<http::dynamic_body>;

class Exchange {
  public:
    Exchange(std::string name, const std::string& http_host)
        : m_name(std::move(name))
    {
        init_http(http_host);
    }

    void init_http(std::string const& host)
    {
        const auto results{resolver.resolve(host, "443")};
        get_lowest_layer(stream).connect(results);
        // Set SNI Hostname (many hosts need this to handshake successfully)
        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};
        }
        stream.handshake(ssl::stream_base::client);
    }

    void init_webSocket(std::string const& host, std::string const& port,
                        const char* p = "")
    {
        // Set SNI Hostname (many hosts need this to handshake successfully)
        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");
        auto const results = resolver_webSocket.resolve(host, port);
        net::connect(ws.next_layer().next_layer(), results.begin(),
                     results.end());
        ws.next_layer().handshake(ssl::stream_base::client);

        ws.handshake(host, p);
    }

    void read_Socket() { ws.read(buffer); }

    bool is_socket_open()
    {
        if (ws.is_open())
            return true;
        return false;
    }

    void write_Socket(const std::string& text) { ws.write(net::buffer(text)); }

    std::string get_socket_data()
    {
        return beast::buffers_to_string(buffer.data());
    }
    void buffer_clear() { buffer.clear(); }

    void webSocket_close() { ws.close(websocket::close_code::none); }

  private:
    // HTTP REQUEST SET //
    std::string     m_name;
    net::io_context ioc;
    ssl::context    ctx{ssl::context::tlsv12_client};
    tcp::resolver   resolver{ioc};
    Stream          stream{ioc, ctx};

    // WEB SOCKET SET //
    std::string        m_web_socket_host;
    std::string        m_web_socket_port;
    beast::flat_buffer buffer;
    net::io_context    ioc_webSocket;
    ssl::context       ctx_webSocket{ssl::context::tlsv12_client};
    tcp::resolver      resolver_webSocket{ioc_webSocket};
    websocket::stream<beast::ssl_stream<tcp::socket>> ws{ioc_webSocket,
                                                         ctx_webSocket};
};

int main()
{
    Exchange kraken{"kraken", "ws.kraken.com"};

    try {
        kraken.init_webSocket("ws.kraken.com", "443", "/");
        if (kraken.is_socket_open())
            kraken.write_Socket(
                R"({"event": "subscribe","pair": ["MINA/USD"],"subscription": {"name": "spread"}})");
        while (true) {
            kraken.read_Socket();
            std::cout << kraken.get_socket_data();

            kraken.buffer_clear();
        }
        kraken.webSocket_close();
    } catch (std::exception const& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
}
于 2021-10-06T13:47:09.207 回答
0

您是否遇到过与您发布过的相同的错误,但在此代码块的最后一行?

        ws.next_layer().handshake(ssl::stream_base::client);
        // Set a decorator to change the User-Agent of the handshake
        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");
        }));
        ws.handshake(host, target.c_str());

对于 bitfinex,我不得不干预主机和目标,直到参数起作用,但我目前正在尝试为 Deribit 设置它,但我什么也做不了。我本来以为

ws.handshake("deribit.com", "/ws/api/v2");

本来可以的,但到目前为止还没有。我还尝试在此参数之前以及第一次设置 SNI 主机名。

于 2022-01-16T08:53:08.030 回答