Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I am relatively new to C and C++ programming and I am trying to connect to a server using Websockets using the boost and beast libraries in C++. I followed the tutorial here but I get the following error

terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
  what():  The WebSocket handshake was declined by the remote peer [boost.beast.websocket:20]

This is my code so far. I need help figuring out the problem and also if you guys can include resources where I can become better at C++ and networking in general I would really appreciate it.

#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <rapidjson/allocators.h>
#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
#include <ta_libc.h>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
using namespace rapidjson;
namespace beast = boost::beast;
namespace http = beast::http;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = boost::asio::ip::tcp;
int main() {
    string host = "ws-feed.exchange.coinbase.com";
    auto const port = "443";
    const char doc[] =
        "{ \'type\': \'subscribe\', \'product_ids\': [\'ETH-USD\'], \'channels\': [\'matches\'] }";
    Document document;
    document.Parse(doc);
    net::io_context ioc;
    tcp::resolver resolver{ioc};
    websocket::stream<tcp::socket> ws{ioc};
    auto const results = resolver.resolve(host, port);
    auto ep = net::connect(ws.next_layer(), results);
    host += ":" + to_string(ep.port());
    ws.set_option(websocket::stream_base::decorator(
        [](websocket::request_type& req)
                req.set(http::field::user_agent,
                string(BOOST_BEAST_VERSION_STRING) +
                " websocket-client-coro");
    ws.handshake(host, "/");
    ws.write(net::buffer(string(doc)));
    beast::flat_buffer buffer;
    ws.read(buffer);
    ws.close(websocket::close_code::normal);
    cout << beast::make_printable(buffer.data()) << endl;
} // main
                Please don't delete and repost your questions, you can edit your question Israel l instead
– Alan Birtles
                May 25, 2022 at 6:30
                @AlanBirtles Port 443 is the HTTPS port. Which might be the problem, the OP is trying to connect to the HTTPS port but without the SSL/TLS parts that's needed for it.
– Some programmer dude
                May 25, 2022 at 6:30
                Yes, and that handshake was declined by the remote peer says it. If you would like an example of using SSL, I've done it here.
– lakeweb
                May 26, 2022 at 0:13

Like others said, you don't use SSL where the server requires it.

Here's a demo. The subtler point is that the server required SNI.

#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for header-only lib
#include <iostream>
namespace beast     = boost::beast;
namespace http      = beast::http;
namespace websocket = beast::websocket;
namespace net       = boost::asio;
namespace ssl       = net::ssl;
namespace json      = boost::json;
using tcp = boost::asio::ip::tcp;
int main() {
    std::string host = "ws-feed.exchange.coinbase.com";
    auto const  port = "443";
    json::value doc = {
        {"type", "subscribe"},
        {"product_ids", {"ETH-USD"}},
        {"channels", {"matches"}},
    net::io_context ioc;
    tcp::resolver   resolver{ioc};
    ssl::context    ctx(ssl::context::sslv23_client);
    ctx.set_default_verify_paths();
    websocket::stream<ssl::stream<tcp::socket>> ws{ioc, ctx};
    auto const results = resolver.resolve(host, port);
    // connect raw socket
    auto& raw = beast::get_lowest_layer(ws);
    auto ep = net::connect(raw, results);
    // 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 boost::system::system_error(
            ::ERR_get_error(), boost::asio::error::get_ssl_category());
    ws.next_layer().handshake(ssl::stream_base::client);
    // ssl handshake
    host += ":" + std::to_string(ep.port());
    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");
    // websocket handshake
    ws.handshake(host, "/");
    std::cout << doc << std::endl;
    ws.write(net::buffer(serialize(doc)));
    beast::flat_buffer buffer;
    ws.read(buffer);
    std::cout << beast::make_printable(buffer.data()) << std::endl;
    ws.close(websocket::close_code::normal);

Results in:

{"type":"subscribe","product_ids":["ETH-USD"],"channels":["matches"]}
{"type":"subscriptions","channels":[{"name":"matches","product_ids":["ETH-USD"]}]}
terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
  what():  stream truncated [asio.ssl.stream:1]

The error indicates that the server doesn't gracefully shutdown the connection. That's probably by design.

Because it rejected the handshake. Not all servers require SNI, in fact for a large period of time SNI didn't exist. However in the world of shared hosting SNI made a lot of sense, so it has quietly taken over, and in general it's a good fist line of defense against rogue traffic. – sehe Jun 26, 2022 at 16:48 @user997112 Indeed. The SSL was documented, the SNI requires an educated guess. The Beast examples show this consistently, I think, so it's possible to "accidentally" get it even without knowledge of SNI – sehe Jun 26, 2022 at 20:23 @user997112 Good point, it's the call to SSL_set_tlsext_host_name I will add a comment accordingly. The beast examples already do this, see e.g. boost.org/doc/libs/1_67_0/libs/beast/example/http/client/… – sehe Jun 26, 2022 at 23:16

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.