Long polling vs WebSocket en C++ : La bataille pour le temps réel
\n\nImaginez : vous écrivez un chat en C++, un terminal boursier ou un système de surveillance de serveurs. Le client doit recevoir les données instantanément. Les requêtes HTTP « demande-réponse » ne conviennent pas – trop lentes et coûteuses. Deux solutions s'offrent à vous : Long polling (la vieille école) et WebSocket (le standard moderne). Que choisir pour un projet C++ ? Analysons cela avec des exemples concrets.
\n\nComment fonctionne le Long polling en C++ (et pourquoi il est toujours utilisé)
\n\nLe Long polling est une émulation du temps réel via HTTP standard. Le client envoie une requête, le serveur ne répond pas immédiatement, mais « reste en attente » jusqu'à l'arrivée de nouvelles données ou un délai d'attente. Dès que les données arrivent – le serveur envoie la réponse, le client envoie immédiatement une nouvelle requête. Cela crée l'illusion d'un flux continu.
\n\nEn C++, cette approche est souvent implémentée via libcurl côté client et un serveur HTTP quelconque (par exemple, cpp-httplib ou Boost.Beast) côté serveur. Voici à quoi ressemble la partie serveur classique avec 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 // Méthode thread-safe pour ajouter un 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 // Gestionnaire de requête Long Polling\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 // Attendre un nouveau message ou un délai d'attente de 30 secondes\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 // Délai d'attente – retourner une réponse vide\n res.result(http::status::no_content);\n res.body() = "";\n }\n res.prepare_payload();\n }\n};\n\nLe client avec libcurl ressemblera à ceci :
\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); // légèrement supérieur au délai d'attente du serveur\n\n CURLcode res = curl_easy_perform(curl);\n if (res == CURLE_OK && !response.empty()) {\n std::cout << "Reçu : " << response << std::endl;\n response.clear();\n // envoyer immédiatement une nouvelle requête\n continue;\n }\n // En cas d'erreur, attendre 5 secondes et réessayer\n std::this_thread::sleep_for(std::chrono::seconds(5));\n }\n}\n\nAvantages du Long polling en C++ : Simplicité, compatibilité avec tous les proxys et pare-feux, ne nécessite pas de bibliothèques spéciales. Inconvénients : Nombre énorme d'en-têtes HTTP, latences liées à l'établissement de la connexion (TCP ha