Forem

Cover image for Building Your First C++ WebSocket Client
Shridhar G Vatharkar
Shridhar G Vatharkar

Posted on

Building Your First C++ WebSocket Client

Welcome to our step-by-step tutorial on accessing real-time Forex, CFD, and Crypto data using TraderMade’s WebSocket with C++. In the fast-paced financial world, staying updated with live data is essential for making informed decisions. This guide will walk you through the entire process seamlessly.

Getting Started

First, sign up for a TraderMade account and get a free API key for a 2-week trial. This key is your gateway to real-time market data. Once you have it, you’ll use it to authenticate your requests. Reviewing the Streaming Forex API documentation will also help you understand the available data and how to use it effectively.

Setting Up Your Coding Environment

C++ development is more convenient in a Linux environment. You can install the Ubuntu app from the search bar if you're using Windows.

For those new to C++, installing the GCC compiler is essential for running C++ programs. We’ll break everything down into simple steps to make the setup process smooth and efficient.

Let’s dive in and start building!

sudo apt update
Enter fullscreen mode Exit fullscreen mode

If you haven’t set a password or can't remember it, you can reset it using the following command:

sudo passwd
Enter fullscreen mode Exit fullscreen mode

With our system up to date, let's go ahead and install the essential tools for compilation by running the following command:

sudo apt install build-essential
Enter fullscreen mode Exit fullscreen mode

To check the version of GCC you're working with, run the following command:

gcc --version
Enter fullscreen mode Exit fullscreen mode

Now that the C++ compiler is set up, let's import the necessary libraries to run our program.

git clone https://github.com/zaphoyd/websocketpp.git
cd websocketpp
Enter fullscreen mode Exit fullscreen mode

This will fetch the WebSocket++ library directly from its GitHub repository. Additionally, we’ll need to install two more libraries: Boost and SSL. You can do so by running the following commands:

sudo apt-get install libboost-all-dev
sudo apt-get install libssl-dev
Enter fullscreen mode Exit fullscreen mode

Writing Code

Now, let's include the libraries necessary to run our program.

#include <websocketpp/config/asio_client.hpp> 
#include <websocketpp/client.hpp> 
#include <iostream> 
Enter fullscreen mode Exit fullscreen mode

Next, we'll define aliases using typedef for easier reference in our code. The websocketpp is a namespace within the WebSocket++ library. The first line represents a complex type that enables WebSocket client support with Asio (for asynchronous I/O) and TLS (for secure communication).

In the second line, shared_ptr is a smart pointer from the WebSocket++ library that automatically manages memory for the object. The type of object it will manage is specified after the < symbol. context_ptr is an alias that simplifies using the longer type in the second line, making the code more readable and concise.

typedef websocketpp::client<websocketpp::config::asio_tls_client> client;
typedef websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context> context_ptr;
Enter fullscreen mode Exit fullscreen mode

The code below should now be clearer. In the first two lines, _1 and _2 represent the first and second arguments, which are passed to the WebSocket++ function objects and callbacks. The third line defines the bind function, which allows you to create function objects using placeholders.

using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
Enter fullscreen mode Exit fullscreen mode

Now that we've defined the placeholders and bound them to callback functions for handling WebSocket events, we'll explain the functions for each event that we might encounter after establishing a connection.

First, we define the on_open callback function, which is triggered when a WebSocket connection is successfully made. It takes two parameters: a handle to the connection (hdl) and a pointer to the WebSocket client (client* c).
The second line of the code is a simple print statement. Following that, we include an error code for error handling and a connection pointer (connection_ptr). The if statement checks for any errors when retrieving the connection pointer. We send a string payload as text over the WebSocket connection if no error is found.

void on_open(websocketpp::connection_hdl hdl, client* c) {
    std::cout << "WebSocket connection opened!" << std::endl;

    websocketpp::lib::error_code ec;
    client::connection_ptr con = c->get_con_from_hdl(hdl, ec);
    if (ec) {
        std::cout << "Failed to get connection pointer: " << ec.message() << std::endl;
        return;
    }
    std::string payload = "{"userKey":"API_KEY", "symbol":"EURUSD,GBPUSD"}";
    c->send(con, payload, websocketpp::frame::opcode::text);
}
Enter fullscreen mode Exit fullscreen mode

Now that we're familiar with the on_open callback function, the next part of the code is easy to grasp. The on_message callback function also takes two parameters; the second is message_ptr msg, which contains the Forex data received. We then print this data inside the on_message function.

The on_fail and on_close callbacks are straightforward and don't require much explanation to proceed to the next section of the code.

void on_message(websocketpp::connection_hdl, client::message_ptr msg) {
    std::cout << "Received message: " << msg->get_payload() << std::endl;

}
void on_fail(websocketpp::connection_hdl hdl) {
    std::cout << "WebSocket connection failed!" << std::endl;
}
void on_close(websocketpp::connection_hdl hdl) {
    std::cout << "WebSocket connection closed!" << std::endl;
}
Enter fullscreen mode Exit fullscreen mode

The next function configures the SSL context for secure communication in the WebSocket client. We won’t delve into the specifics of this function; instead, we’ll jump straight into the main function, where everything integrates.

context_ptr on_tls_init(const char * hostname, websocketpp::connection_hdl) {
    context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);

    try {
        ctx->set_options(boost::asio::ssl::context::default_workarounds |
                         boost::asio::ssl::context::no_sslv2 |
                         boost::asio::ssl::context::no_sslv3 |
                         boost::asio::ssl::context::single_dh_use);

        ctx->set_verify_mode(boost::asio::ssl::verify_none);
    } catch (std::exception& e) {
        std::cout << "TLS Initialization Error: " << e.what() << std::endl;
    }
    return ctx;
}
Enter fullscreen mode Exit fullscreen mode

In the main function, we define the client, then specify the URL to connect to the Forex WebSocket and retrieve sub-second data.

int main(int argc, char* argv[]) {
    client c;

    std::string hostname = "marketdata.tradermade.com/feedadv";
    std::string uri = "wss://" + hostname;
}
Enter fullscreen mode Exit fullscreen mode

We set up a try block inside the main function to handle possible exceptions.

int main(int argc, char* argv[]) {
....
try {

    } catch (websocketpp::exception const & e) {
        std::cout << "WebSocket Exception: " << e.what() << std::endl;
    }

}
Enter fullscreen mode Exit fullscreen mode

Within the try block, we begin by configuring the WebSocket++ client and initializing Asio for asynchronous operations. Next, we set up message and event handlers, including the TLS (Transport Layer Security) initialization handler.

We then declare an error object and check for issues while retrieving the connection. Lastly, we create a connection using c.connect(con) and initiate the WebSocket event loop with c.run(), which handles the asynchronous events.

try {
      // Configure WebSocket++ client
      c.set_access_channels(websocketpp::log::alevel::all);
      c.clear_access_channels(websocketpp::log::alevel::frame_payload);
      c.set_error_channels(websocketpp::log::elevel::all);

      c.init_asio();

      // Set message, TLS initialization, open, fail, and close handlers
      c.set_message_handler(&on_message);

      c.set_tls_init_handler(bind(&on_tls_init, hostname.c_str(), ::_1));

      c.set_open_handler(bind(&on_open, ::_1, &c));
      c.set_fail_handler(bind(&on_fail, ::_1));


      c.set_close_handler(bind(&on_close, ::_1));
      // Enable detailed error logging

      c.set_error_channels(websocketpp::log::elevel::all);          

      websocketpp::lib::error_code ec;

      client::connection_ptr con = c.get_connection(uri, ec);

      if (ec) {
                 std::cout << "Could not create connection because: " << ec.message() << std::endl;
                 return 0;
              }

      // Create a connection to the specified url
      c.connect(con);

      c.run();

    } catch (websocketpp::exception const & e) {
      std::cout << "WebSocket Exception: " << e.what() << std::endl;
    }
Enter fullscreen mode Exit fullscreen mode

After writing the code, save it in a file named FX_WebSocket.cpp (or any name you prefer). Then, compile the program using the following command:

g++ -o ./streaming_forex  FX_WebSocket.cpp -lssl -lcrypto -pthread
Enter fullscreen mode Exit fullscreen mode

Your program should now be compiled into the streaming_forex file. To run the file, use the following command:

./streaming_forex
Enter fullscreen mode Exit fullscreen mode

Voila! You should now see a WebSocket connection with your subscribed Forex data. Remember to replace your API key when sending the payload using the on_open function.

Output

Great! Our output is ready.

Received message: Connected
Received message: {"symbol":"GBPUSD","ts":"1708403061263","bid":1.25867,"ask":1.25871,"mid" :1.25869}
Received message: {"symbol":"GBPUSD","ts":"1708403070164","bid":1.25867,"ask":1.2587,"mid":1.258685}
Received message: {"symbol":"GBPUSD","ts":"1708403070951","bid":1.25866,"ask":1.2587,"mid":1.25868}
Received message: {"symbol":"GBPUSD","ts":"1708403071022","bid":1.25866,"ask":1.25871,"mid":1.258685}
Received message: {"symbol":"GBPUSD","ts":"1708403071213","bid":1.25866,"ask":1.2587,"mid":1.25868}
Received message: {"symbol":"GBPUSD","ts":"1708403071400","bid":1.25866,"ask":1.25869,"mid":1.258675}
Received message: {"symbol":"EURUSD","ts":"1708403071512","bid":1.07687,"ask":1.07691,"mid":1.07689}
Received message: {"symbol":"EURUSD","ts":"1708403071524","bid":1.07687,"ask":1.0769,"mid":1.076885}
Received message: {"symbol":"GBPUSD","ts":"1708403072138","bid":1.25866,"ask":1.2587,"mid":1.25868}
Received message: {"symbol":"GBPUSD","ts":"1708403074548","bid":1.25866,"ask":1.25869,"mid":1.258675}
Received message: {"symbol":"GBPUSD","ts":"1708403074618","bid":1.25866,"ask":1.2587,"mid":1.25868}
Received message: {"symbol":"GBPUSD","ts":"1708403074921","bid":1.25866,"ask":1.25869,"mid":1.258675}
Received message: {"symbol":"GBPUSD","ts":"1708403075348","bid":1.25866,"ask":1.2587,"mid":1.25868}
Received message: {"symbol":"GBPUSD","ts":"1708403077083","bid":1.25866,"ask":1.25869,"mid":1.258675}
Enter fullscreen mode Exit fullscreen mode

Congratulations! You've successfully built a real-time Forex data fetcher using C++ and WebSocket++. Feel free to explore and adjust the code to suit your needs, and enjoy access to live financial data at your fingertips.

Full Code

Below is the complete code:

#include <websocketpp/config/asio_client.hpp>
#include <websocketpp/client.hpp>
#include <iostream>

typedef websocketpp::client<websocketpp::config::asio_tls_client> client;
typedef websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context> context_ptr;

using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;

void on_open(websocketpp::connection_hdl hdl, client* c) {
    std::cout << "WebSocket connection opened!" << std::endl;

     websocketpp::lib::error_code ec;
    client::connection_ptr con = c->get_con_from_hdl(hdl, ec);

    if (ec) {
        std::cout << "Failed to get connection pointer: " << ec.message() << std::endl;
        return;
    }

  std::string payload = "{"userKey":"API_KEY", "symbol":"EURUSD,GBPUSD"}";
    c->send(con, payload, websocketpp::frame::opcode::text);
}

void on_message(websocketpp::connection_hdl, client::message_ptr msg) {
    std::cout << "Received message: " << msg->get_payload() << std::endl;
}

void on_fail(websocketpp::connection_hdl hdl) {
    std::cout << "WebSocket connection failed!" << std::endl;
}

void on_close(websocketpp::connection_hdl hdl) {
    std::cout << "WebSocket connection closed!" << std::endl;
}

context_ptr on_tls_init(const char * hostname, websocketpp::connection_hdl) {
    context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);

    try {
        ctx->set_options(boost::asio::ssl::context::default_workarounds |
                         boost::asio::ssl::context::no_sslv2 |
                         boost::asio::ssl::context::no_sslv3 |
                         boost::asio::ssl::context::single_dh_use);

    } catch (std::exception& e) {
        std::cout << "TLS Initialization Error: " << e.what() << std::endl;
    }


    return ctx;
}

int main(int argc, char* argv[]) {
    client c;

    std::string hostname = "marketdata.tradermade.com/feedadv";
    std::string uri = "wss://" + hostname;


    try {
        c.set_access_channels(websocketpp::log::alevel::all);
        c.clear_access_channels(websocketpp::log::alevel::frame_payload);
        c.set_error_channels(websocketpp::log::elevel::all);
        c.init_asio();


        c.set_message_handler(&on_message);
        c.set_tls_init_handler(bind(&on_tls_init, hostname.c_str(), ::_1));

        c.set_open_handler(bind(&on_open, ::_1, &c));
        c.set_fail_handler(bind(&on_fail, ::_1));
        c.set_close_handler(bind(&on_close, ::_1));
        c.set_error_channels(websocketpp::log::elevel::all);  // Enable detailed error logging

        websocketpp::lib::error_code ec;
        client::connection_ptr con = c.get_connection(uri, ec);
        if (ec) {
            std::cout << "Could not create connection because: " << ec.message() << std::endl;
            return 0;
        }

        c.connect(con);

        c.run();
    } catch (websocketpp::exception const & e) {
        std::cout << "WebSocket Exception: " << e.what() << std::endl;
    }

}
Enter fullscreen mode Exit fullscreen mode

Next Steps

TraderMade’s Live Streaming API is an easy-to-use and powerful resource for building digital solutions and gaining valuable insights. You can use it in C++ to create impressive applications, and we’d love to see what you make!

Register and explore the details in the Live Streaming API documentation to get started. If you need any help, feel free to reach out via live chat or email us at support@tradermade.com. We're here to help you on your development journey.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.