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
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With