Compilation Error with Boost Beast Websocket in C++ Project

151 Views Asked by At

I'm working on a C++ project where I'm using Boost Beast for implementing a WebSocket client. The project is intended to connect to a cryptocurrency exchange's WebSocket API and process market data. However, I'm encountering a compilation error related to Boost Beast and Boost Asio with SSL streams. Below is the relevant part of the code and the error message I received.

Code:

#include <array>
#include <atomic>
#include <thread>
#include <cstdlib>
#include <string>
#include <thread>
#include <iostream>
#include <sstream>
#include <curl/curl.h>
#include <shared_mutex>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <nlohmann/json.hpp>

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

// ... [Other parts of the code like the OrderBook class]

class WebSocketClient
{
private:
    using Socket = tcp::socket;
    using Stream = ssl::stream<Socket>;
    using WS = websocket::stream<Stream>;

    net::io_context ioc_;
    OrderBook &orderBook_;
    std::string coin_;
    tcp::resolver resolver_{ioc_};
    ssl::context ssl_context_{ssl::context::tlsv12_client};
    WS ws_{ioc_, ssl_context_};
    boost::beast::flat_buffer buffer_;

public:
    WebSocketClient(OrderBook &orderBook, std::string coin) : orderBook_(orderBook), coin_(std::move(coin))
    {
        ssl_context_.set_default_verify_paths();
        ws_.next_layer().set_verify_mode(ssl::verify_peer);
        ws_.next_layer().set_verify_callback(ssl::rfc2818_verification("stream.binance.com"));
    }

    void read()
    {
        ws_.async_read(
            buffer_,
            [this](boost::system::error_code ec, std::size_t bytes_transferred)
            {
                onRead(ec, bytes_transferred);
            });
    }

    void send(std::string msg)
    {
        ws_.write(net::buffer(msg));
    }

    void connectAndSubscribe()
    {
        // Resolve the host name and connect to the server
        auto results = resolver_.resolve("stream.binance.com", "9443");
        auto ep = connect(ws_.next_layer().next_layer(), results);
        auto host_ = "stream.binance.com:" + std::to_string(ep.port());

        ws_.next_layer().handshake(Stream::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"); }));

        std::string path = "/ws/" + coin_ + "@depth5@100ms";
        ws_.handshake(host_, path);
        std::cout << "Handshake successful." << std::endl;

        json j_msg = {
            {"method", "SUBSCRIBE"},
            {"params", {coin_ + "@depth5@100ms"}},
            {"id", 1}};

        std::string msg = j_msg.dump(); // Serialize JSON to string
        send(msg);
        std::cout << "Subscription request done!" << std::endl;
        read();
    }

    void onRead(boost::system::error_code ec, std::size_t bytes_transferred)
    {
        if (ec)
        {
            std::cerr << "Read error: " << ec.message() << std::endl;
            return;
        }

        // Process the received message
        std::string message = boost::beast::buffers_to_string(buffer_.data());
        buffer_.consume(bytes_transferred);

        try
        {
            json j = json::parse(message);
            // Process JSON message and update OrderBook
            std::cout << "Reading request done!" << std::endl;
            processMessage(j);
        }
        catch (const json::exception &e)
        {
            std::cerr << "JSON parsing error: " << e.what() << std::endl;
        }

        // Continue reading messages
        read();
    }

    void processMessage(const json &j)
    {
        if (j.contains("lastUpdateId") && j.contains("bids") && j.contains("asks"))
        {
            int index = 0;
            for (const auto &bid : j["bids"])
            {
                float price = stof(bid[0].get<string>());
                float quantity = stof(bid[1].get<string>());

                orderBook_.update(index++, price);
                orderBook_.update(index++, quantity);
            }
            index = 10; // Start index for asks

            for (const auto &ask : j["asks"])
            {
                float price = stof(ask[0].get<string>());
                float quantity = stof(ask[1].get<string>());

                orderBook_.update(index++, price);
                orderBook_.update(index++, quantity);
            }
        }
    }

};

Error Message:

/usr/local/include/boost/beast/websocket/teardown.hpp:105:5: error: static assertion failed due to requirement 'sizeof(boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>>) == -1': Unknown Socket type in async_teardown.

Compilation Command: I am using CMake with Ninja as the build system.I have already tried updating Boost and OpenSSL to the latest versions, but the issue persists. Any insights into what might be causing this compilation error and how to resolve it would be greatly appreciated.

1

There are 1 best solutions below

0
On

The error originates here:

void read() {
    ws_.async_read(buffer_, [this](error_code ec, size_t n) { onRead(std::move(ec), n); });
}

It's 100% caused by the fact that you forget to include

#include <boost/beast/ssl.hpp>

That's not even counting the things just missing from the question:

namespace net       = boost::asio;
namespace ssl       = net::ssl;
namespace http      = boost::beast::http;
namespace websocket = boost::beast::websocket;
using net::ip::tcp;
using json = nlohmann::json;

struct OrderBook {
    void update(int, float) {}
};

Luckily there's also a bunch of redundant/unused includes:

// #include <array>
// #include <cstdlib>
// #include <curl/curl.h>
// #include <sstream>
// #include <string>

Here your code, once again made self-contained:

Live On Compiler Explorer

// #include <array>
// #include <cstdlib>
// #include <curl/curl.h>
// #include <sstream>
// #include <string>
#include <atomic>
#include <iostream>
#include <nlohmann/json.hpp>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <shared_mutex>
#include <thread>

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

namespace net       = boost::asio;
namespace ssl       = net::ssl;
namespace http      = boost::beast::http;
namespace websocket = boost::beast::websocket;
using net::ip::tcp;
using json = nlohmann::json;

struct OrderBook {
    void update(int, float) {}
};

class WebSocketClient {
  private:
    using error_code = boost::beast::error_code;
    using Socket     = tcp::socket;
    using Stream     = ssl::stream<Socket>;
    using WS         = websocket::stream<Stream>;

    net::io_context ioc_;
    OrderBook&      orderBook_;
    std::string     coin_;

    tcp::resolver             resolver_{ioc_};
    ssl::context              ssl_context_{ssl::context::tlsv12_client};
    WS                        ws_{ioc_, ssl_context_};
    boost::beast::flat_buffer buffer_;

  public:
    WebSocketClient(OrderBook& orderBook, std::string coin) : orderBook_(orderBook), coin_(std::move(coin)) {
        ssl_context_.set_default_verify_paths();
        ws_.next_layer().set_verify_mode(ssl::verify_peer);
        ws_.next_layer().set_verify_callback(ssl::rfc2818_verification("stream.binance.com"));
    }

    void send(std::string msg) { ws_.write(net::buffer(msg)); }

    void connectAndSubscribe() {
        // Resolve the host name and connect to the server
        auto results = resolver_.resolve("stream.binance.com", "9443");
        auto ep      = connect(ws_.next_layer().next_layer(), results);
        auto host_   = "stream.binance.com:" + std::to_string(ep.port());

        ws_.next_layer().handshake(Stream::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");
        }));

        std::string path = "/ws/" + coin_ + "@depth5@100ms";
        ws_.handshake(host_, path);
        std::cout << "Handshake successful." << std::endl;

        json j_msg = {
            {"method", "SUBSCRIBE"},
            {"params", {coin_ + "@depth5@100ms"}},
            {"id", 1}};

        std::string msg = j_msg.dump(); // Serialize JSON to string
        send(msg);
        std::cout << "Subscription request done!" << std::endl;
        readLoop();
    }

  private:
    void readLoop() {
        ws_.async_read(buffer_, [this](error_code ec, size_t n) { onRead(std::move(ec), n); });
    }

    void onRead(error_code ec, size_t bytes_transferred) {
        if (ec) {
            std::cerr << "Read error: " << ec.message() << std::endl;
            return;
        }

        // Process the received message
        std::string message = boost::beast::buffers_to_string(buffer_.data());
        buffer_.consume(bytes_transferred);

        try {
            json j = json::parse(message);
            // Process JSON message and update OrderBook
            std::cout << "Reading request done!" << std::endl;
            processMessage(j);
        } catch (json::exception const& e) {
            std::cerr << "JSON parsing error: " << e.what() << std::endl;
        }

        // Continue reading messages
        readLoop();
    }

    void processMessage(json const& j) {
        if (j.contains("lastUpdateId") && j.contains("bids") && j.contains("asks")) {
            int index = 0;
            for (auto const& bid : j["bids"]) {
                float price    = stof(bid[0].get<std::string>());
                float quantity = stof(bid[1].get<std::string>());

                orderBook_.update(index++, price);
                orderBook_.update(index++, quantity);
            }
            index = 10; // Start index for asks

            for (auto const& ask : j["asks"]) {
                float price    = stof(ask[0].get<std::string>());
                float quantity = stof(ask[1].get<std::string>());

                orderBook_.update(index++, price);
                orderBook_.update(index++, quantity);
            }
        }
    }
};

int main() {
    //
}