Long polling vs WebSocket in C++: The Battle for Real-Time
\n\nImagine: you are writing a chat in C++, a stock exchange terminal, or a server monitoring system. The client must receive data instantly. HTTP "request-response" requests are not suitable — too slow and wasteful. There are two options: Long polling (old school) and WebSocket (modern standard). What to choose for a C++ project? Let's figure it out with real examples.
\n\nHow Long Polling Works in C++ (and Why It's Still Alive)
\n\nLong polling is an emulation of real-time over regular HTTP. The client sends a request, the server does not respond immediately, but "hangs" until new data appears or a timeout occurs. As soon as data appears — the server sends a response, the client immediately sends a new request. An illusion of a continuous stream is created.
\n\nIn C++, this approach is often implemented via libcurl on the client and any HTTP server (e.g., cpp-httplib or Boost.Beast) on the server. Here is what a classic server-side part looks like on Boost.Beast:
#include <boost/beast/core.hpp>\n#include <boost/beast/http.hpp>\n#include <boost/beast/version.hpp>\n#include <boost/asio.hpp>\n#include <queue>\n#include <mutex>\n#include <condition_variable>\n\nnamespace beast = boost::beast;\nnamespace http = beast::http;\nnamespace net = boost::asio;\n\nclass LongPollHandler {\n std::queue<std::string> messages_;\n std::mutex mtx_;\n std::condition_variable cv_;\npublic:\n // Thread-safe method for adding a message\n void push(const std::string& msg) {\n std::lock_guard<std::mutex> lock(mtx_);\n messages_.push(msg);\n cv_.notify_one();\n }\n\n // Long Polling request handler\n void handle(http::request<http::string_body>& req,\n http::response<http::string_body>& res) {\n std::unique_lock<std::mutex> lock(mtx_);\n // Wait for a new message or a 30-second timeout\n if (cv_.wait_for(lock, std::chrono::seconds(30),\n [this](){ return !messages_.empty(); })) {\n std::string msg = messages_.front();\n messages_.pop();\n res.result(http::status::ok);\n res.body() = msg;\n } else {\n // Timeout — return an empty response\n res.result(http::status::no_content);\n res.body() = "";\n }\n res.prepare_payload();\n }\n};\n\nThe client on libcurl will look like this:
\n\n#include <curl/curl.h>\n#include <string>\n#include <iostream>\n\nsize_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) {\n ((std::string*)userp)->append((char*)contents, size * nmemb);\n return size * nmemb;\n}\n\nvoid long_poll_loop(CURL* curl, const std::string& url) {\n std::string response;\n while (true) {\n curl_easy_setopt(curl, CURLOPT_URL, url.c_str());\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);\n curl_easy_setopt(curl, CURLOPT_TIMEOUT, 35L); // slightly more than the server timeout\n\n CURLcode res = curl_easy_perform(curl);\n if (res == CURLE_OK && !response.empty()) {\n std::cout << "Received: " << response << std::endl;\n response.clear();\n // immediately send a new request\n continue;\n }\n // On error, wait 5 seconds and retry\n std::this_thread::sleep_for(std::chrono::seconds(5));\n }\n}\n\nPros of Long polling in C++: Simplicity, compatibility with all proxies and firewalls, no special libraries required. Cons: Huge number of HTTP headers, connection setup delays (TCP ha