WebSocket — один из самых распространенных транспортов для обмена данными в реальном времени: чаты, биржевые котировки, игровые серверы, IoT. На практике выбор библиотеки редко сводится к вопросу «кто быстрее парсит заголовок фрейма». Важнее сочетание совместимости со старым набором инструментов сборки, поддержки TLS, сжатия per-message-deflate, модели асинхронности или блокировки, размера бинарника и способа обработки ошибок.
В репозитории wscpp лежат две связанные, но независимые реализации одного протокольного стека:
-
wscpp — C++11, клиент и сервер, лицензия MIT;
-
ws-rs — Rust-компаньон с той же послойной архитектурой и теми же регрессионными векторами RFC 6455 §5.7.
Дисклеймер: обе библиотеки экспериментальные и созданы с помощью AI-агентов; люди курируют тесты, CI и релизы. Ниже — не рекламный обзор, а структурированный сравнительный анализ с воспроизводимыми цифрами.
Дисклеймер: задержки и пропускная способность — 7 июня 2026; размеры ELF перепроверены 8 июня 2026. Платформа: Linux/WSL2 (GCC 15), Release; в LAN — Debian 13 VM, RTT ~0,5–0,7 мс. В сравнительных тестах TLS не использовался (но код TLS/deflate включен в сборку). На каждый прогон — одно соединение.
Методология
Порог совместимости с RFC
Бенчмарки запускаются только после прохождения набора обязательных требований:
Сценарии измерений
|
Сценарий |
Что измеряем |
|---|---|
|
Задержка эхо-ответа (p50/p99) |
100 циклов «отправил — получил» текстовым ping-сообщением на localhost и в LAN |
|
Пропускная способность 64 KiB |
100 итераций бинарных сообщений |
|
Парсинг и сборка фреймов |
1 MiB, локальный микробенчмарк (только CPU) |
|
Задержка подключения |
Только для клиентских библиотек (easywsclient) |
Ограничения: одно соединение на прогон; в сравнительном наборе нет TLS; результаты в LAN зависят от реальной сети — на localhost доминируют микрооперации парсера, в LAN — сетевая карта и планировщик ОС.
Размер бинарника: что именно сравниваем
Во всех таблицах — размер ELF-файла тестового бенчмарка (stat -c%s на bench_*_roundtrip), Release-сборка. Это не «полностью статический» артефакт и не суммарный объем зависимостей на диске.
|
Стек |
Что попадает в ELF |
Что остается снаружи (динамические |
|---|---|---|
|
wscpp (C++) |
код библиотеки ( |
OpenSSL ( |
|
ws-rs (Rust) |
rustls, ring, flate2, tokio (если включен) |
в основном |
|
libwebsockets |
тонкая обертка |
|
Следствия для читателя:
-
C++ — бинарник выглядит меньше, потому что криптография (OpenSSL ~7 МБ в системных
.so) не входит в ELF. На «голой» системе без OpenSSL эти.soпридется поставлять отдельно. -
Rust — бинарник больше, как файл, но самодостаточнее: TLS и deflate вшиты в ELF.
-
Прямое сравнение «286 КБ C++ vs 1,9 МБ Rust» по размеру файла некорректно без этой оговорки. Для embedded смотрите не только ELF, но и то, есть ли OpenSSL в образе.
-
C++ — замеры:
WSCPP_ENABLE_LOGGING=OFF(по умолчанию логирование включено и дает ~650 КБ вместо ~286 КБ). Rust-замеры:[profile.release]сlto = "fat",strip = trueвrust/Cargo.toml.
Часть 1. Экосистема WebSocket для C++11
Критерий отбора
В каталог попали библиотеки с официальной поддержкой C++11 (или C API, вызываемого из C++11 без более нового стандарта). Решения, требующие C++17 и выше (uWebSockets, Poco Net 1.13+, seasocks), исключены.
Первый уровень: полноценные клиент и сервер
|
Библиотека |
C++11 |
TLS |
Асинхронность |
Зависимости |
|---|---|---|---|---|
|
websocketpp |
да |
да |
ASIO |
ASIO, OpenSSL |
|
Boost.Beast |
да |
да |
Boost.Asio |
Boost, OpenSSL |
|
IXWebSocket |
да |
да |
потоки |
zlib, OpenSSL/MbedTLS |
|
Simple-WebSocket-Server |
да |
да |
ASIO |
ASIO, OpenSSL |
|
wscpp |
да |
да |
ASIO или POSIX |
ASIO† или только OpenSSL |
† ASIO 1.20 подтягивается через FetchContent при WSCPP_USE_ASIO=ON.
Второй и третий уровни: минималисты и C-стеки
-
easywsclient (~600 строк) — только блокирующий клиент, без TLS;
-
libwebsockets — зрелый production-стек на C, но с другой моделью интеграции.
Результаты на localhost (задержка эхо и размер бинарника)
|
Библиотека |
p50 |
p99 |
64 KiB |
Размер бинарника |
|---|---|---|---|---|
|
wscpp (linux POSIX) |
0,25 мс |
0,36 мс |
92 МБ/с |
286 КБ† |
|
wscpp (ASIO) |
0,25 мс |
0,32 мс |
82 МБ/с |
383 КБ† |
|
websocketpp 0.8.2 |
0,31 мс |
0,59 мс |
— |
687 КБ |
|
IXWebSocket 11.4.6 |
0,28 мс |
0,68 мс |
62 МБ/с |
473 КБ |
|
libwebsockets 4.3.5 |
0,26 мс |
0,40 мс |
— |
126 КБ* |
|
Boost.Beast 1.88 |
0,25 мс |
0,32 мс |
68 МБ/с |
667 КБ |
|
Simple-WebSocket-Server |
0,28 мс |
0,46 мс |
— |
675 КБ |
* libwebsockets линкуется с системной .so — размер ELF нельзя напрямую сравнивать с аналогами, где OpenSSL подключается отдельно.
† размер ELF; OpenSSL и zlib — динамические .so (см. методологию).
Результаты в локальной сети (две машины, plain ws://)
|
Библиотека |
p50 |
p99 |
64 KiB |
|---|---|---|---|
|
wscpp (linux) |
0,34 мс |
1,24 мс |
27 МБ/с |
|
wscpp (ASIO) |
0,40 мс |
0,77 мс |
28 МБ/с |
|
websocketpp |
0,32 мс |
2,01 мс |
— |
|
IXWebSocket |
0,42 мс |
0,85 мс |
31 МБ/с |
|
libwebsockets |
0,33 мс |
3,55 мс |
— |
|
Beast |
0,32 мс |
0,86 мс |
29 МБ/с |
Вывод по C++: по медиане (p50) на localhost все библиотеки первого уровня укладываются в ~0,25–0,31 мс. Различия заметнее в хвосте распределения (p99): websocketpp и libwebsockets на LAN показывают более высокие значения. Особняком стоят размер ELF и модель API. Транспорт wscpp на POSIX-сокетах дает самый компактный файл бенчмарка среди полнофункциональных C++ — стеков — при условии, что OpenSSL уже есть в системе.
wscpp: два транспорта
# Минимальный ELF — POSIX, OpenSSL динамическиcmake -B build-linux -DWSCPP_BUILD_BENCHMARKS=ON \ -DWSCPP_USE_ASIO=OFF -DWSCPP_ENABLE_LOGGING=OFF# Кроссплатформенный ASIOcmake -B build-asio -DWSCPP_BUILD_BENCHMARKS=ON \ -DWSCPP_USE_ASIO=ON -DWSCPP_ENABLE_LOGGING=OFF
|
Транспорт |
p50 |
p99 |
64 KiB |
ELF (КиБ) |
|---|---|---|---|---|
|
linux POSIX |
0,25 мс |
0,36 мс |
92 МБ/с |
286 КБ |
|
ASIO |
0,25 мс |
0,32 мс |
82 МБ/с |
383 КБ |
Микробенчмарки слоя фреймов (не зависят от транспорта):
|
Операция |
Пропускная способность |
|---|---|
|
Сборка фрейма |
~15 ГБ/с |
|
Парсинг фрейма |
~22 ГБ/с |
|
XOR-маскирование |
~70 ГБ/с |
Часть 2. Экосистема WebSocket для Rust
Первый уровень: асинхронные клиент и сервер на tokio
|
Библиотека |
Среда выполнения |
TLS |
Лицензия |
|---|---|---|---|
|
tokio-tungstenite |
tokio |
rustls/native-tls |
MIT |
|
fastwebsockets |
tokio / ручной режим |
rustls |
Apache-2.0 |
|
tokio-websockets |
tokio |
rustls |
MIT |
|
ws-rs |
tokio + опционально блокирующий режим |
rustls |
MIT |
Результаты на localhost
|
Библиотека |
p50 |
p99 |
64 KiB |
ELF (КиБ) |
|---|---|---|---|---|
|
ws-rs (tokio) |
0,26 мс |
0,37 мс |
78 МБ/с |
2311 КБ |
|
ws-rs (блокирующий) |
0,25 мс |
0,31 мс |
84 МБ/с |
1895 КБ |
|
tokio-tungstenite |
0,30 мс |
0,41 мс |
76 МБ/с |
856 КБ |
|
fastwebsockets |
0,29 мс |
0,55 мс |
83 МБ/с |
1037 КБ |
|
tokio-websockets |
0,31 мс |
0,44 мс |
85 МБ/с |
779 КБ |
Результаты в локальной сети
|
Библиотека |
p50 |
p99 |
64 KiB |
|---|---|---|---|
|
ws-rs (tokio) |
0,35 мс |
0,51 мс |
30 МБ/с |
|
ws-rs (блокирующий) |
0,40 мс |
0,51 мс |
30 МБ/с |
|
tokio-tungstenite |
0,37 мс |
0,67 мс |
30 МБ/с |
|
fastwebsockets |
0,36 мс |
0,60 мс |
30 МБ/с |
|
tokio-websockets |
0,38 мс |
0,67 мс |
30 МБ/с |
Вывод по Rust: tokio-tungstenite — де-факто стандарт экосистемы. fastwebsockets и tokio-websockets конкурируют по пропускной способности на localhost. По размеру ELF ws-rs тяжелее wscpp: в Rust-бинарник вшиты rustls, ring и flate2, тогда как у C++ OpenSSL остается в .so. Блокирующий bench_blocking_roundtrip (~1,9 МБ) меньше tokio-варианта (~2,3 МБ), но все равно крупнее C++ ELF (~286 КБ) — зато автономнее по зависимостям.
Замеры Rust — через cargo build --release -p ws-rs-benches --bins (все default-features ws-rs). Для библиотеки без tokio в своем приложении:
cargo build -p ws-rs --release --no-default-features \ --features "std-blocking,deflate,blocking-tls"
Размер итогового бинарника приложения будет меньше, чем у тестового bench_blocking_roundtrip, который собирается с полным набором возможностей для сопоставимости с C++ — стеком.
В LAN все четыре библиотеки сходятся к ~30 МБ/с — упираются в сеть, а не в парсер.
Часть 3. C++ и Rust: одна архитектура, разные компромиссы
Проект wscpp — редкий случай, когда один протокольный стек реализован дважды с общими регрессионными векторами и зеркальными тестовыми стендами.
Архитектура
|
Слой |
wscpp (C++) |
ws-rs (Rust) |
|---|---|---|
|
Фреймы |
|
|
|
Рукопожатие |
|
|
|
Соединение |
поток + транспорт |
tokio |
|
Публичный API |
|
|
|
Ошибки |
|
|
|
TLS |
OpenSSL |
rustls |
Паритет функций
|
Возможность |
wscpp |
ws-rs |
|---|---|---|
|
RFC 6455 |
да |
да |
|
wss:// |
OpenSSL |
rustls |
|
per-message-deflate |
да (v1.1.0) |
да (v0.3.0+) |
|
Два транспорта |
ASIO / linux POSIX |
tokio / std-blocking |
|
Векторы §5.7 |
6 тестов |
6 тестов (портированы) |
Прямое сравнение на localhost
|
Метрика |
wscpp (linux) |
wscpp (ASIO) |
ws-rs (tokio) |
ws-rs (блокирующий) |
|---|---|---|---|---|
|
p50 |
0,25 мс |
0,25 мс |
0,26 мс |
0,25 мс |
|
p99 |
0,36 мс |
0,32 мс |
0,37 мс |
0,31 мс |
|
64 KiB |
92 МБ/с |
82 МБ/с |
78 МБ/с |
84 МБ/с |
|
ELF |
286 КБ† |
383 КБ† |
2311 КБ |
1895 КБ |
|
Динамические |
OpenSSL, zlib |
OpenSSL, zlib |
|
|
† OpenSSL не входит в ELF — см. методологию.
Микробенчмарки слоя фреймов
|
Операция |
wscpp (ориентир) |
ws-rs до v0.4.x |
ws-rs v0.4.x |
|---|---|---|---|
|
Сборка 1 MiB |
~15 ГБ/с |
8886 МБ/с |
~16 100 МБ/с |
|
Парсинг 1 MiB |
~22 ГБ/с |
19701 МБ/с |
~17 100 МБ/с* |
|
XOR-маскирование |
~70 ГБ/с |
49664 МБ/с |
~62 600 МБ/с |
* путь парсера не менялся — разброс ±15 % на WSL2, операция упирается в memcpy.
Что дал проход оптимизаций ws-rs (v0.4.x)
После выхода на функциональный паритет с wscpp слой ws-rs прошел отдельный проход по скорости (все — в безопасном Rust, unsafe_code = "forbid"):
-
Профиль релиза:
lto = "fat",codegen-units = 1,strip— межмодульная оптимизация и подстановка функций; тесты остаются на dev-профиле, раскрутка стека при панике сохранена. -
Кодировщик фрейма в один проход: полезная нагрузка копируется в выходной буфер один раз и маскируется на месте — было до трех копий на отправку, стало одна.
-
Parser::take_frame(): разобранная нагрузка отдается черезmem::take, без клонирования. -
Постоянный буфер чтения с курсором (чанк 64 KiB) в обоих транспортах: нет аллокации
Vecна каждыйread_frame, курсор убираетdrainна каждый байт, крупные чтения экономят системные вызовы. Попутно устранена скрытая потеря фреймов, пришедших «в хвосте» одногоread(). -
Cow<[u8]>на отправке: на несжатом (типичном) пути нетto_vec().
Итог: сборка фрейма выросла ~в 1,85 раза и вплотную подошла к C++ — ориентиру (~16 против ~15 ГБ/с), XOR-маскирование — ~в 1,3 раза. Пропускная способность 64 KiB на localhost поднялась с 74 до ~78 МБ/с (tokio) и с 79 до ~84 МБ/с (блокирующий режим). На сквозной задержке эхо разница по-прежнему сглаживается: доминируют системные вызовы, планировщик и копирование в буфер сокета. Те же приемы — маскирование на месте, передача владения вместо O(n)-копии — применяет и fastwebsockets; дополнительно RUSTFLAGS="-C target-cpu=native" включает более широкую автовекторизацию маски (ценой переносимости бинарника).
Плюсы и минусы реализаций
wscpp (C++)
Плюсы:
-
C++11 без Boost — встраивается в унаследованный код и embedded-сборки;
-
Два транспорта: linux POSIX (286 КБ ELF) и ASIO (кроссплатформа);
-
std::error_codeна всех путях ввода-вывода — без исключений; удобно при-fno-exceptions; -
Самый компактный ELF среди полнофункциональных C++ — аналогов (OpenSSL — в
.so); -
Явное разделение на слои frame → connection → client/server; 94 автотеста в репозитории;
-
ASIO 1.20 подключается через FetchContent без ручной установки зависимостей.
Минусы:
-
Экспериментальный статус, код сгенерирован с помощью AI — перед продакшеном нужна независимая проверка;
-
Меньше примеров и расширений, чем у websocketpp, Beast или libwebsockets;
-
Проверка клиентского сертификата по умолчанию отключена (
verify_none) — удобно для разработки, не для продакшена; -
per-message-deflate включается явно через
enable_permessage_deflate(); -
На LAN p99 у linux-транспорта (1,24 мс) хуже, чем у ASIO (0,77 мс) — вероятная проблема с хвостом распределения в транспорте на POSIX poll.
ws-rs (Rust)
Плюсы:
-
Идиоматичный Rust:
Result, владение, документация на все публичные элементы, предупреждения clippy трактуются как ошибки в CI; -
Паритет с wscpp — общие RFC-векторы, зеркальные скрипты бенчмарков;
-
Два режима: tokio и блокирующий
std::netбез runtime; -
Безопасность памяти на уровне языка; на протокольных путях нет panic;
-
Оптимизированные горячие пути (v0.4.x): кодировщик фрейма в один проход, буфер чтения с курсором,
Cowна отправке — без аллокаций на сообщение, все в безопасном Rust; -
Микробенчмарки фреймов — одни из лучших показателей в наборе (~63 ГБ/с XOR-маскирование, ~16 ГБ/с сборка фрейма).
Минусы:
-
Крупный ELF (~2,3 МБ tokio, ~1,9 МБ blocking) — rustls/ring/flate2 внутри файла; выбор runtime заметно влияет на размер;
-
Молодая библиотека, та же экспериментальная оговорка;
-
Меньше сообщества, чем у tokio-tungstenite;
-
API с опросом (
read_message,recv_text) — не обратные вызовы, как в C++; при миграции придется переписать цикл обработки событий.
Сравнение экосистем (широкий взгляд)
|
Критерий |
C++ |
Rust |
|---|---|---|
|
Legacy C++11, минимум зависимостей |
wscpp linux / easywsclient |
ws-rs std-blocking |
|
Максимум возможностей |
websocketpp, Beast |
tokio-tungstenite |
|
Мобильные / кроссплатформенные SDK |
IXWebSocket |
— |
|
Зрелый C-стек для эксплуатации |
libwebsockets |
— |
|
Без исключений |
wscpp |
Rust по умолчанию |
|
Минимальный ELF бенчмарка (полный стек) |
wscpp 286 КБ (+ OpenSSL |
ws-rs blocking 1895 КБ (TLS внутри) |
|
Кросс-языковая регрессия RFC |
wscpp + ws-rs вместе |
wscpp + ws-rs вместе |
Практические рекомендации
|
Задача |
Рекомендация |
|---|---|
|
Embedded / минимальный ELF на C++11 (OpenSSL в системе) |
wscpp |
|
Embedded без OpenSSL в образе |
сравните суммарный объем: C++ ELF + |
|
Существующий ASIO-проект |
wscpp ASIO или websocketpp |
|
Rust + tokio микросервис |
tokio-tungstenite (зрелость) или ws-rs (RFC-паритет с wscpp) |
|
Блокирующий Rust без async runtime |
ws-rs |
|
Продакшен без ревью кода |
Ни wscpp, ни ws-rs — сначала аудит; для C++ смотрите Beast/libwebsockets |
|
Нужен per-message-deflate |
wscpp 1.1+, IXWebSocket, Beast, libwebsockets, ws-rs 0.3+ |
Как воспроизвести
C++ — оба транспорта и сравнение с аналогами:
# Для сопоставимых размеров ELF отключите логирование:cmake -B build-bench-linux -DWSCPP_BUILD_BENCHMARKS=ON \ -DWSCPP_USE_ASIO=OFF -DWSCPP_ENABLE_LOGGING=OFF -DCMAKE_BUILD_TYPE=Releasecmake --build build-bench-linux --target run_benchmarks -j"$(nproc)"bash benchmarks/run_benchmarks_both.shbash benchmarks/run_remote_network_compare.sh
Rust:
bash rust/benchmarks/run_benchmarks.shbash rust/benchmarks/run_remote_network_compare.sh
Полные таблицы и журнал измерений: ANALYSIS.md, ANALYSIS_RUST.md.
Заключение
Экосистемы WebSocket на C++11 и Rust не делятся на «быстрые» и «медленные» на типичных сценариях эхо-ответа: медиана на localhost у всех библиотек первого уровня укладывается в доли миллисекунды. Реальный выбор определяется сопутствующими факторами:
-
Стандарт языка и toolchain (C++11 vs Rust 2021);
-
Модель параллелизма (обратные вызовы + поток vs async/await vs блокирующий
std::net); -
Размер и модель линковки (286 КБ ELF wscpp + OpenSSL
.sovs 1,9–2,3 МБ ws-rs с TLS внутри ELF); -
Обработка ошибок (
error_codevsResult); -
Зрелость и сообщество (Beast, libwebsockets, tokio-tungstenite vs экспериментальные wscpp/ws-rs).
Пара wscpp + ws-rs полезна как RFC реализация на двух языках: один набор RFC-требований, общие регрессионные векторы, сопоставимые тестовые стенды — редкий инструмент для проверки «правильно ли мы понимаем RFC 6455», а не только для выбора библиотеки в продакшене.
ссылка на оригинал статьи https://habr.com/ru/articles/1044686/