Введение: Почему C++ до сих пор рулит в real-time?
Когда речь заходит о real-time приложениях — чатах, биржевых стаканах, многопользовательских играх или системах мониторинга — многие сразу думают о Node.js или Python с asyncio. И зря. C++ остаётся королём производительности и низкой задержки. Если вам нужно обрабатывать тысячи соединений с минимальным latency, WebSockets в C++ — это выбор профессионалов.
В этой статье мы разберём, как поднять real-time сервер на C++, используя мощные библиотеки. Вы узнаете, в чём преимущество C++ перед скриптовыми языками, и получите готовые примеры кода, которые можно сразу использовать в проектах.
Мы не будем ходить вокруг да около: откроем WebSocket-соединение, отправим сообщение и закроем сессию. А заодно посмотрим, как это всё работает под капотом. Поехали!
Что такое WebSocket и почему он нужен в C++?
WebSocket — это протокол, который позволяет установить постоянное двунаправленное соединение между клиентом и сервером. В отличие от HTTP, где каждый запрос — это отдельная транзакция, WebSocket держит «трубу» открытой. Это идеально для real-time приложений C++, где каждая миллисекунда на счету.
Основные сценарии использования:
- Финансовые терминалы: обновление котировок в реальном времени.
- Игровые сервера: синхронизация состояния игроков.
- Системы мониторинга: передача логов и метрик с датчиков.
- Коллаборативные редакторы: совместное редактирование документов.
C++ даёт здесь неоспоримое преимущество: прямой контроль над памятью, отсутствие GC (сборщика мусора) и возможность использовать асинхронные паттерны без оверхеда виртуальной машины. Если вам нужно обрабатывать 50 000+ одновременных соединений, WebSocket сервер на C++ будет стабильнее и быстрее аналогов.
Выбираем библиотеку: Boost.Beast или uWebSockets?
На рынке C++ есть два основных игрока для работы с WebSocket. Давайте разберём их плюсы и минусы.
Boost.Beast — стандарт индустрии
Boost.Beast — это библиотека на основе Boost.Asio. Она даёт полный контроль над сетевым стеком. Её используют в продакшене такие гиганты, как Facebook (в некоторых внутренних инструментах) и финансовые компании.
- Плюсы: максимальная гибкость, поддержка HTTP+WebSocket, интеграция с Asio.
- Минусы: высокий порог входа, много кода для простых задач.
uWebSockets — лёгкость и скорость
uWebSockets (uWS) — это обёртка над C-библиотекой uSockets. Она спроектирована для максимальной производительности и минимального потребления памяти. Идеально для микросервисов.
- Плюсы: очень быстрый, простой API, низкое потребление RAM.
- Минусы: меньше сообщество, сложнее кастомизировать.
Для нашего примера мы возьмём Boost.Beast, так как он даёт больше возможностей для обучения. Но если вам нужно простое real-time приложение на C++ под нагрузкой — присмотритесь к uWS.
Пишем простой WebSocket сервер на C++ с Boost.Beast
Давайте создадим минимальный эхо-сервер. Он будет принимать соединения, читать сообщения и отправлять их обратно. Это классический «Hello World» для WebSocket.
Убедитесь, что у вас установлен Boost (желательно версии 1.75+) и компилятор с поддержкой C++17.
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <string>
#include <thread>
namespace beast = boost::beast;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = net::ip::tcp;
// Обработка одного WebSocket-соединения
void do_session(tcp::socket& socket) {
try {
websocket::stream<tcp::socket&> ws{socket};
ws.accept();
for (;;) {
beast::flat_buffer buffer;
ws.read(buffer);
// Отправляем обратно то, что получили
ws.text(ws.got_text());
ws.write(buffer.data());
}
} catch (beast::system_error const& se) {
if (se.code() != websocket::error::closed) {
std::cerr << "Error: " << se.code().message() << std::endl;
}
}
}
int main() {
try {
net::io_context ioc{1};
tcp::acceptor acceptor{ioc, tcp::endpoint(tcp::v4(), 8080)};
std::cout << "WebSocket сервер запущен на порту 8080" << std::endl;
for (;;) {
tcp::socket socket{ioc};
acceptor.accept(socket);
// Запускаем обработку в отдельном потоке
std::thread{std::bind(&do_session, std::move(socket))}.detach();
}
} catch (std::exception const& e) {
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
}
Что здесь происходит:
- Мы создаём TCP-акцептор на порту 8080.
- При подключении клиента вызывается
do_session. - Внутри функции мы выполняем
ws.accept()— это рукопожатие WebSocket. - Затем в бесконечном цикле читаем сообщения и отправляем их обратно.
Этот код — основа для любого WebSocket сервера на C++. Вы можете модифицировать его под свои задачи: добавить парсинг JSON, роутинг или авторизацию.
Создаём WebSocket клиент на C++
Сервер без клиента — как замок без ключа. Давайте напишем простой клиент, который подключается к нашему серверу и отправляет сообщение.
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <iostream>
namespace beast = boost::beast;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = net::ip::tcp;
int main() {
try {
net::io_context ioc;
tcp::resolver resolver{ioc};
websocket::stream<tcp::socket> ws{ioc};
auto const results = resolver.resolve("127.0.0.1", "8080");
net::connect(ws.next_layer(), results.begin(), results.end());
ws.handshake("127.0.0.1:8080", "/");
ws.write(net::buffer(std::string("Привет,