Long polling vs WebSocket в C++: что выбрать для реального времени

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Long polling vs WebSocket в C++: Битва за реальное время

Представьте: вы пишете чат на C++, биржевой терминал или систему мониторинга серверов. Клиент должен получать данные мгновенно. HTTP-запросы «спросил-ответил» не подходят — слишком медленно и расточительно. Выхода два: Long polling (старая школа) и WebSocket (современный стандарт). Что выбрать для C++ проекта? Давайте разбираться на реальных примерах.

Как работает Long polling в C++ (и почему он жив до сих пор)

Long polling — это эмуляция real-time поверх обычного HTTP. Клиент отправляет запрос, сервер не отвечает сразу, а «висит» до появления новых данных или таймаута. Как только данные появляются — сервер шлёт ответ, клиент сразу отправляет новый запрос. Создаётся иллюзия непрерывного потока.

В C++ такой подход часто реализуют через libcurl на клиенте и любой HTTP-сервер (например, cpp-httplib или Boost.Beast) на сервере. Вот как выглядит классическая серверная часть на Boost.Beast:

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio.hpp>
#include <queue>
#include <mutex>
#include <condition_variable>

namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;

class LongPollHandler {
    std::queue<std::string> messages_;
    std::mutex mtx_;
    std::condition_variable cv_;
public:
    // Потокобезопасный метод добавления сообщения
    void push(const std::string& msg) {
        std::lock_guard<std::mutex> lock(mtx_);
        messages_.push(msg);
        cv_.notify_one();
    }

    // Обработчик Long Polling запроса
    void handle(http::request<http::string_body>& req,
                http::response<http::string_body>& res) {
        std::unique_lock<std::mutex> lock(mtx_);
        // Ждём новое сообщение или таймаут 30 секунд
        if (cv_.wait_for(lock, std::chrono::seconds(30),
                         [this](){ return !messages_.empty(); })) {
            std::string msg = messages_.front();
            messages_.pop();
            res.result(http::status::ok);
            res.body() = msg;
        } else {
            // Таймаут — возвращаем пустой ответ
            res.result(http::status::no_content);
            res.body() = "";
        }
        res.prepare_payload();
    }
};

Клиент на libcurl будет выглядеть так:

#include <curl/curl.h>
#include <string>
#include <iostream>

size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) {
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}

void long_poll_loop(CURL* curl, const std::string& url) {
    std::string response;
    while (true) {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 35L); // чуть больше серверного таймаута

        CURLcode res = curl_easy_perform(curl);
        if (res == CURLE_OK && !response.empty()) {
            std::cout << "Получено: " << response << std::endl;
            response.clear();
            // сразу шлём новый запрос
            continue;
        }
        // При ошибке ждём 5 секунд и повторяем
        std::this_thread::sleep_for(std::chrono::seconds(5));
    }
}

Плюсы Long polling в C++: Простота, совместимость со всеми прокси и фаерволлами, не требует специальных библиотек. Минусы: Огромное количество HTTP-заголовков, задержки на установку соединения (TCP handshake при каждом запросе), сервер держит много потоков в ожидании.

WebSocket в C++: Настоящий real-time на Boost.Beast

WebSocket — это полноценный двунаправленный канал поверх TCP. После рукопожатия (Upgrade-запрос) клиент и сервер обмениваются фреймами без оверхеда HTTP. Для C++ лучшая библиотека — Boost.Beast (часть Boost.Asio). Она даёт полный контроль над сокетами и асинхронностью.

Пример WebSocket-сервера на C++ с использованием Boost.Beast:

#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio.hpp>
#include <memory>
#include <string>
#include <iostream>

namespace beast = boost::beast;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = net::ip::tcp;

// Обработчик одного подключения
class WebSocketSession : public std::enable_shared_from_this<WebSocketSession> {
    websocket::stream<beast::tcp_stream> ws_;
    beast::flat_buffer buffer_;
public:
    explicit WebSocketSession(tcp::socket&& socket)
        : ws_(std::move(socket)) {}

    void run() {
        // Асинхронно принимаем WebSocket рукопожатие
        ws_.async_accept(
            beast::bind_front_handler(&WebSocketSession::on_accept, shared_from_this()));
    }

    void on_accept(beast::error_code ec) {
        if (ec) return;
        // Начинаем читать сообщения
        do_read();
    }

    void do_read() {
        ws_.async_read(buffer_,
            beast::bind_front_handler(&WebSocketSession::on_read, shared_from_this()));
    }

    void on_read(beast::error_code ec, std::size_t bytes_transferred) {
        if (ec) return;
        // Эхо-ответ: отправляем полученное сообщение обратно
        ws_.text(ws_.got_text());
        ws_.async_write(buffer_.data(),
            beast::bind_front_handler(&WebSocketSession::on_write, shared_from_this()));
    }

    void on_write(beast::error_code ec, std::size_t bytes_transferred) {
        if (ec) return;
        buffer_.consume(buffer_.size());
        do_read(); // читаем следующее сообщение
    }

    // Метод для отправки данных из другого потока (например, из бизнес-логики)
    void send(const std::string& msg) {
        ws_.text(true);
        ws_.write(net::buffer(msg));
    }
};

// Асинхронный слушатель
class WebSocketServer {
    net::io_context& ioc_;
    tcp::acceptor acceptor_;
public:
    WebSocketServer(net::io_context& ioc, short port)
        : ioc_(ioc), acceptor_(ioc, tcp::endpoint(tcp::v4(), port)) {
        do_accept();
    }

    void do_accept() {
        acceptor_.async_accept(
            [this](beast::error_code ec, tcp::socket socket) {
                if (!ec) {
                    std::make_shared<WebSocketSession>(std::move(socket))->run();
                }
                do_accept();
            });
    }
};

Клиентская часть на C++ (Boost.Beast):

#include <boost/beast/core.hpp>
#include <                

Блоги

Рекомендаций по книгам