Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C or C++ websocket client working example

I am new to C and C++. I am trying to find small working example for any websocket library in C or C++ that can connect to websocket server. So, far I have explored, uWebsockets, libwebsockets, websocketpp, and boost::beast. None of them seem to have detailed documentation. I found some examples on boost::beast website at https://www.boost.org/doc/libs/develop/libs/beast/doc/html/beast/examples.html, however they are not working either. If I can find make a single working example I can work on it to learn more.

I tried this command, and it is connecting to yahoo endpoint: wscat -c "wss://streamer.finance.yahoo.com/" -H 'Origin: https://finance.yahoo.com' and print a random string.

wscat -c "wss://streamer.finance.yahoo.com/" -H 'Origin: https://finance.yahoo.com'
Connected (press CTRL+C to quit)
> {"subscribe":["ES=F","YM=F","NQ=F","RTY=F","CL=F","GC=F","SI=F","EURUSD=X","^TNX","^VIX","GBPUSD=X","JPY=X","BTC-USD","^CMC200","^FTSE","^N225","INTC"]}
< CgdCVEMtVVNEFduJQ0cYoP2/2/VeIgNVU0QqA0NDQzApOAFFlmEuP0iAgL/AwQJVlwxHR139ST1HZYBWqUNqC0JpdGNvaW4gVVNEsAGAgL/AwQLYAQTgAYCAv8DBAugBgIC/wMEC8gEDQlRD+gENQ29pbk1hcmtldENhcIECAAAAADbvcUGJAgAAhAG9ZWtC
< CgdCVEMtVVNEFQTtQkcY4KbH2/VeIgNVU0QqA0NDQzApOAFFUznHPkiAgMzPwQJVlwxHR139ST1HZQBrQUNqC0JpdGNvaW4gVVNEsAGAgMzPwQLYAQTgAYCAzM/BAugBgIDMz8EC8gEDQlRD+gENQ29pbk1hcmtldENhcIECAAAAADbvcUGJAgAAND7DT2tC

I tried simple python code like this

from websocket import create_connection
import json
import pprint
import re
import time
import datetime



def subscribe_yahoo ():        
    headers = {
        'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0',
        'Accept': '*/*',
        'Accept-Language': 'en-US,en;q=0.5',
        'Sec-WebSocket-Version': '13',
        'Origin': 'https://finance.yahoo.com',
        'Sec-WebSocket-Key': 'nNtGm/0ZJcrR+goawlJz9w==',
        'DNT': '1',
        'Connection': 'keep-alive, Upgrade',
        'Sec-Fetch-Dest': 'websocket',
        'Sec-Fetch-Mode': 'websocket' ,
        'Sec-Fetch-Site': 'same-site' ,
        'Pragma': 'no-cache',
        'Cache-Control': 'no-cache',
        'Upgrade': 'websocket',
    }


    messages='{"subscribe":["INTC"]}'

    # Initialize the headers needed for the websocket connection
    initMessages = [
       messages,
       
    ]


    
    websocketUri = """wss://streamer.finance.yahoo.com/"""
    print (websocketUri)
           
    ws = create_connection(websocketUri,header=headers)
    for m in initMessages:
        print ("sending ", m)
        ws.send(m)
            
    message_stream = True
    i=0
    while message_stream:
        result = ws.recv()
        i=i+1
        print (str(i),' -- ', result)


subscribe_yahoo()

and it is working too.

I would really appreciate if someone can help me with a code that works similarly in c or c++.

Can someone explain if it is possible to use firefox source code https://searchfox.org/mozilla-central/source/netwerk/protocol/websocket to implement a websocket client in C++ or not, or if someone has used firefox code successfully for websocket client.

I haven't asked for any recommended library, any library will do for my learning purpose. Thanks in advance :)

Following example is copied as is from https://www.boost.org/doc/libs/develop/libs/beast/example/websocket/client/sync-ssl/websocket_client_sync_ssl.cpp

#include "example/common/root_certificates.hpp"

#include <boost/beast/core.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <cstdlib>
#include <iostream>
#include <string>

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;       // from <boost/asio/ip/tcp.hpp>

// Sends a WebSocket message and prints the response
int main(int argc, char** argv)
{
    try
    {
        // Check command line arguments.
        if(argc != 4)
        {
            std::cerr <<
                "Usage: websocket-client-sync-ssl <host> <port> <text>\n" <<
                "Example:\n" <<
                "    websocket-client-sync-ssl echo.websocket.org 443 \"Hello, world!\"\n";
            return EXIT_FAILURE;
        }
        std::string host = argv[1];
        auto const  port = argv[2];
        auto const  text = argv[3];

        // The io_context is required for all I/O
        net::io_context ioc;

        // The SSL context is required, and holds certificates
        ssl::context ctx{ssl::context::tlsv12_client};

        // This holds the root certificate used for verification
        load_root_certificates(ctx);

        // These objects perform our I/O
        tcp::resolver resolver{ioc};
        websocket::stream<beast::ssl_stream<tcp::socket>> ws{ioc, ctx};

        // Look up the domain name
        auto const results = resolver.resolve(host, port);

        // Make the connection on the IP address we get from a lookup
        auto ep = net::connect(get_lowest_layer(ws), 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 beast::system_error(
                beast::error_code(
                    static_cast<int>(::ERR_get_error()),
                    net::error::get_ssl_category()),
                "Failed to set SNI Hostname");

        // Update the host_ string. This will provide the value of the
        // Host HTTP header during the WebSocket handshake.
        // See https://tools.ietf.org/html/rfc7230#section-5.4
        host += ':' + std::to_string(ep.port());

        // Perform the SSL handshake
        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");
            }));

        // Perform the websocket handshake
        ws.handshake(host, "/");

        // Send the message
        ws.write(net::buffer(std::string(text)));

        // This buffer will hold the incoming message
        beast::flat_buffer buffer;

        // Read a message into our buffer
        ws.read(buffer);

        // Close the WebSocket connection
        ws.close(websocket::close_code::normal);

        // If we get here then the connection is closed gracefully

        // The make_printable() function helps print a ConstBufferSequence
        std::cout << beast::make_printable(buffer.data()) << std::endl;
    }
    catch(std::exception const& e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

Compiled using :

g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

g++ boost_test.cpp -o websocket-client-sync-ssl -lboost_system -pthread -lssl -lcrypto

./websocket-client-sync-ssl
Usage: websocket-client-sync-ssl <host> <port> <text>
Example:
    websocket-client-sync-ssl echo.websocket.org 443 "Hello, world!"

then as suggested:

./websocket-client-sync-ssl echo.websocket.org 443 "Hello, world!"

doesn't work

/websocket-client-sync-ssl streamer.finance.yahoo.com 443 "Hello, world!"
Error: The WebSocket stream was gracefully closed at both endpoints
like image 230
Michael Auten Avatar asked Mar 31 '26 18:03

Michael Auten


2 Answers

Here's a quick demo using easywsclient:

#include "easywsclient.hpp"
#include <iostream>
#include <string>
#include <memory>
#include <mutex>
#include <deque>
#include <thread>
#include <chrono>
#include <atomic>

// a simple, thread-safe queue with (mostly) non-blocking reads and writes
namespace non_blocking {
template <class T>
class Queue {
    mutable std::mutex m;
    std::deque<T> data;
public:
    void push(T const &input) { 
        std::lock_guard<std::mutex> L(m);
        data.push_back(input);
    }

    bool pop(T &output) {
        std::lock_guard<std::mutex> L(m);
        if (data.empty())
            return false;
        output = data.front();
        data.pop_front();
        return true;
    }
};
}

// eastwsclient isn't thread safe, so this is a really simple
// thread-safe wrapper for it.
class Ws {
    std::thread runner;
    non_blocking::Queue<std::string> outgoing;
    non_blocking::Queue<std::string> incoming;
    std::atomic<bool> running { true };

public:
    void send(std::string const &s) { outgoing.push(s); }
    bool recv(std::string &s) { return incoming.pop(s); }

    Ws(std::string url) {
        using easywsclient::WebSocket;

        runner = std::thread([=] {
            std::unique_ptr<WebSocket> ws(WebSocket::from_url(url));
            while (running) {
                if (ws->getReadyState() == WebSocket::CLOSED)
                    break;
                std::string data;
                if (outgoing.pop(data))
                    ws->send(data);
                ws->poll();
                ws->dispatch([&](const std::string & message) {
                    incoming.push(message);
                });
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
            ws->close();
            ws->poll();
        });
    }
    void close() { running = false; }
    ~Ws() { if (runner.joinable()) runner.join(); }
};

int main() {
    Ws socket("ws://localhost:40800");

    std::atomic<bool> run{true};
    auto receiver = std::thread([&] {
        std::string s;
        while (run) {
            if (socket.recv(s))
                std::cout << s << '\n';
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    });

    std::string line;
    while (std::getline(std::cin, line))
        socket.send(line);
    run = false;
    receiver.join();
    socket.close();
}

I tested it with a server using Crow:

// A simple websocket-based echo server
#include "crow_all.h"

int main() { 
    crow::SimpleApp app;

    CROW_ROUTE(app, "/")
        .websocket()
        .onopen([&](crow::websocket::connection& conn){
                CROW_LOG_INFO << "new websocket connection";
                })
        .onclose([&](crow::websocket::connection& conn, const std::string& reason){
                CROW_LOG_INFO << "websocket connection closed: " << reason;
                })
        .onmessage([&](crow::websocket::connection& conn, const std::string& data, bool is_binary){
                    std::cout << "Received message: " << data << "\n";
                    if (is_binary)
                        conn.send_binary(data);
                    else
                        conn.send_text(data);
                });

    app.port(40800)
        .multithreaded()
        .run();
}

I built using this Makefile:

both: client server

INC = -Iexternal/easywsclient/ -Iexternal/crow/build/amalgamate/

LIBS = -leasywsclient -Lexternal/easywsclient -lboost_system -pthread

CXXFLAGS += ${INC}

client: client.o
    ${CXX} -o client client.o ${LIBS}

server: server.o
    ${CXX} -o server server.o ${LIBS}

To test, start the server, then the client. Then you can type random stuff into the client. It'll be sent to the server, printed out there, echoed back to the client, and printed back out there. Pretty much your typical, useless (but enough to prove they're communicating) network demo kind of thing.

like image 148
Jerry Coffin Avatar answered Apr 03 '26 06:04

Jerry Coffin


I dont know how usefull is this going to be.

You can implement your own websocket connections pretty easily.

A websocket client will send you an HTTP request with an upgrade header.

Once you receive it, you can save that socket connection and use it as a regular TCP socket.

Websocket protocol is pretty simple to implement on your own. You can check websocket RFC. https://datatracker.ietf.org/doc/html/rfc6455

I will edit this response later in order to link a micro implementation i did myself in case it can help

like image 35
Julen Dominadas Avatar answered Apr 03 '26 08:04

Julen Dominadas