🔝 Retour au Sommaire
Standalone Asio (section 22.4.1) offre une base réseau solide — sockets TCP/UDP, timers, résolution DNS, coroutines. Mais dès que vos besoins dépassent le transport brut, les questions arrivent : comment faire du HTTP ? Du WebSocket ? Du TLS/SSL ? Comment sérialiser mes données ? Comment gérer des pools de connexions ?
C'est là qu'intervient Boost.Asio et, plus largement, l'écosystème Boost. Boost.Asio est le même moteur que Standalone Asio (même auteur, même code, même architecture), mais intégré dans Boost avec accès à un écosystème de librairies complémentaires qui couvrent les besoins d'un serveur réseau complet.
La pièce maîtresse de cet écosystème est Boost.Beast — une librairie HTTP et WebSocket construite directement au-dessus de Boost.Asio. C'est elle qui transforme Asio d'une librairie de transport en une plateforme capable de servir des API REST et des connexions WebSocket temps réel.
Le code réseau de Boost.Asio est identique à celui de Standalone Asio. La différence se résume essentiellement à :
// Standalone Asio
#include <asio.hpp>
asio::io_context ctx;
asio::ip::tcp::socket socket(ctx);
// Boost.Asio
#include <boost/asio.hpp>
boost::asio::io_context ctx;
boost::asio::ip::tcp::socket socket(ctx); L'espace de noms passe de asio:: à boost::asio::, les headers passent de <asio/...> à <boost/asio/...>, et les types d'erreur utilisent boost::system::error_code au lieu de std::error_code (bien que les versions récentes supportent les deux).
Tout ce que vous avez appris en section 22.4.1 — io_context, coroutines, callbacks, timers, strands — s'applique identiquement avec Boost.Asio.
La valeur ajoutée de Boost réside dans les librairies qui s'intègrent nativement avec Boost.Asio :
| Librairie | Rôle | Pertinence réseau |
|---|---|---|
| Boost.Beast | HTTP/1.1, WebSocket | Serveurs et clients HTTP, API REST, temps réel |
| Boost.Asio SSL | TLS/SSL via OpenSSL | HTTPS, connexions sécurisées |
| Boost.JSON | Parsing/sérialisation JSON | Corps des requêtes/réponses HTTP |
| Boost.URL | Parsing d'URLs | Routage, extraction de paramètres |
| Boost.Log | Logging structuré | Observabilité serveur |
| Boost.Cobalt | Coroutines simplifiées | Alternative aux awaitables natifs d'Asio |
# conanfile.txt
[requires]
boost/1.86.0
[generators]
CMakeDeps
CMakeToolchain conan install . --output-folder=build --build=missingvcpkg install boost-asio boost-beast boost-json boost-urlsudo apt install libboost-all-dev libssl-devLe paquet libssl-dev est nécessaire pour le support TLS/SSL.
cmake_minimum_required(VERSION 3.20)
project(boost_asio_demo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Boost REQUIRED COMPONENTS system)
find_package(OpenSSL REQUIRED)
find_package(Threads REQUIRED)
add_executable(http_server http_server.cpp)
target_link_libraries(http_server PRIVATE
Boost::system
OpenSSL::SSL
OpenSSL::Crypto
Threads::Threads
)💡 Boost.Asio et Boost.Beast sont largement header-only. Le seul composant compilé nécessaire est
Boost::system(pourboost::system::error_code). Avec Boost 1.85+, même cette dépendance devient optionnelle si vous utilisez lesstd::error_codenatifs.
Beast est la raison principale de choisir Boost.Asio plutôt que Standalone Asio. C'est une librairie bas niveau mais complète pour HTTP/1.1 et WebSocket, conçue pour fonctionner au-dessus de n'importe quel stream Asio (TCP, SSL, ou custom).
Beast n'est pas un framework web à la Express.js ou Flask. C'est une librairie de protocole : elle sait parser et générer des messages HTTP, effectuer le handshake WebSocket, et gérer les frames — mais elle ne gère pas le routage, les middlewares, ou les templates. C'est à vous (ou à un framework de plus haut niveau) de structurer l'application.
Ce positionnement bas niveau est un choix délibéré : Beast est conçu pour être la brique de construction d'outils HTTP/WebSocket performants et spécifiques, pas un framework généraliste.
#include <boost/beast.hpp>
namespace beast = boost::beast;
namespace http = beast::http;
namespace websocket = beast::websocket; Les types HTTP principaux :
// Requête HTTP (méthode, cible, version, headers, body)
http::request<http::string_body> req;
req.method(http::verb::get);
req.target("/api/status");
req.version(11); // HTTP/1.1
req.set(http::field::host, "example.com");
req.set(http::field::user_agent, "Beast/1.0");
// Réponse HTTP
http::response<http::string_body> res;
res.result(http::status::ok);
res.version(11);
res.set(http::field::content_type, "application/json");
res.body() = R"({"status": "ok"})";
res.prepare_payload(); // Calcule Content-Length automatiquement Le template http::request<Body> et http::response<Body> est paramétré par le type de corps. Beast fournit plusieurs implémentations :
| Body type | Usage |
|---|---|
string_body |
Corps stocké comme std::string — le plus courant |
dynamic_body |
Corps stocké dans un beast::flat_buffer dynamique |
file_body |
Corps lu/écrit directement depuis/vers un fichier — zéro copie |
empty_body |
Pas de corps (HEAD, réponses 204) |
Voici un serveur HTTP complet qui répond à des requêtes GET sur plusieurs routes :
// http_server.cpp
#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <boost/beast.hpp>
#include <print>
#include <string>
namespace asio = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
using tcp = asio::ip::tcp;
using asio::awaitable;
using asio::use_awaitable;
// --- Routage simple ---
http::response<http::string_body>
handle_request(const http::request<http::string_body>& req) {
http::response<http::string_body> res;
res.version(req.version());
res.set(http::field::server, "Beast-Server/1.0");
if (req.method() != http::verb::get) {
res.result(http::status::method_not_allowed);
res.set(http::field::content_type, "text/plain");
res.body() = "Method not allowed";
res.prepare_payload();
return res;
}
auto target = req.target();
if (target == "/") {
res.result(http::status::ok);
res.set(http::field::content_type, "text/plain");
res.body() = "Hello from Boost.Beast!";
}
else if (target == "/api/status") {
res.result(http::status::ok);
res.set(http::field::content_type, "application/json");
res.body() = R"({"status":"ok","connections":42})";
}
else if (target == "/api/health") {
res.result(http::status::ok);
res.set(http::field::content_type, "application/json");
res.body() = R"({"healthy":true})";
}
else {
res.result(http::status::not_found);
res.set(http::field::content_type, "text/plain");
res.body() = "Not found: " + std::string(target);
}
res.prepare_payload();
return res;
}
// --- Session HTTP (une par connexion) ---
awaitable<void> handle_session(tcp::socket socket) {
beast::flat_buffer buffer; // Buffer de lecture persistant entre requêtes
try {
while (true) {
// Lire une requête HTTP complète
http::request<http::string_body> req;
co_await http::async_read(socket, buffer, req, use_awaitable);
// Vérifier si le client demande la fermeture
bool keep_alive = req.keep_alive();
// Traiter la requête
auto res = handle_request(req);
res.keep_alive(keep_alive);
// Envoyer la réponse
co_await http::async_write(socket, res, use_awaitable);
if (!keep_alive) {
break; // Le client a demandé Connection: close
}
}
} catch (const boost::system::system_error& e) {
if (e.code() != beast::errc::not_connected &&
e.code() != asio::error::eof) {
std::println(stderr, "Session error: {}", e.what());
}
}
// Fermeture propre TCP
beast::error_code ec;
socket.shutdown(tcp::socket::shutdown_send, ec);
}
// --- Listener ---
awaitable<void> listener(tcp::acceptor acceptor) {
while (true) {
auto [ec, socket] = co_await acceptor.async_accept(
asio::as_tuple(use_awaitable));
if (ec) {
if (ec == asio::error::operation_aborted) break;
std::println(stderr, "Accept error: {}", ec.message());
continue;
}
co_spawn(acceptor.get_executor(),
handle_session(std::move(socket)),
asio::detached);
}
}
int main() {
try {
asio::io_context ctx;
asio::signal_set signals(ctx, SIGINT, SIGTERM);
signals.async_wait([&ctx](auto, auto) { ctx.stop(); });
tcp::acceptor acceptor(ctx);
acceptor.open(tcp::v6());
acceptor.set_option(tcp::acceptor::reuse_address(true));
acceptor.set_option(asio::ip::v6_only(false));
acceptor.bind(tcp::endpoint(tcp::v6(), 8080));
acceptor.listen();
std::println("Serveur HTTP sur http://localhost:8080/");
co_spawn(ctx, listener(std::move(acceptor)), asio::detached);
ctx.run();
} catch (const std::exception& e) {
std::println(stderr, "Fatal: {}", e.what());
return 1;
}
}$ curl http://localhost:8080/
Hello from Boost.Beast!
$ curl http://localhost:8080/api/status
{"status":"ok","connections":42}
$ curl http://localhost:8080/api/health
{"healthy":true}
$ curl http://localhost:8080/nonexistent
Not found: /nonexistent
$ curl -X POST http://localhost:8080/
Method not allowedhttp::async_read — Lit une requête HTTP complète (headers + body) depuis le socket. Beast gère en interne le parsing HTTP, la chunked transfer-encoding, et la reconstitution du message — tout ce que vous auriez dû implémenter manuellement avec des sockets bruts.
beast::flat_buffer — Buffer de lecture persistant entre les requêtes sur la même connexion. Beast peut avoir besoin de lire plus de données que nécessaire pour une requête (pipelining HTTP) — le surplus reste dans le buffer pour la requête suivante.
keep_alive — HTTP/1.1 utilise des connexions persistantes par défaut. Le serveur doit respecter le souhait du client (Connection: close ou Connection: keep-alive) pour décider de fermer ou réutiliser la connexion.
prepare_payload() — Calcule et positionne le header Content-Length automatiquement en fonction de la taille du body. Sans cet appel, le client ne saurait pas combien d'octets lire.
Beast est aussi une librairie client HTTP performante :
// http_client.cpp
#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <boost/beast.hpp>
#include <print>
namespace asio = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
using tcp = asio::ip::tcp;
using asio::awaitable;
using asio::use_awaitable;
awaitable<void> fetch(asio::io_context& ctx,
std::string host, std::string port,
std::string target) {
// Résolution + connexion
tcp::resolver resolver(ctx);
auto endpoints = co_await resolver.async_resolve(host, port, use_awaitable);
tcp::socket socket(ctx);
co_await asio::async_connect(socket, endpoints, use_awaitable);
// Construire la requête
http::request<http::empty_body> req{http::verb::get, target, 11};
req.set(http::field::host, host);
req.set(http::field::user_agent, "Beast-Client/1.0");
// Envoyer
co_await http::async_write(socket, req, use_awaitable);
// Lire la réponse
beast::flat_buffer buffer;
http::response<http::string_body> res;
co_await http::async_read(socket, buffer, res, use_awaitable);
// Afficher (conversion en std::string_view car Beast
// utilise boost::core::string_view, non formatable directement)
std::println("HTTP {} {}", static_cast<int>(res.result()),
std::string_view(res.reason()));
for (auto const& field : res) {
std::println(" {}: {}", std::string_view(field.name_string()),
std::string_view(field.value()));
}
std::println("\n{}", res.body());
// Fermeture propre
beast::error_code ec;
socket.shutdown(tcp::socket::shutdown_both, ec);
}
int main() {
asio::io_context ctx;
co_spawn(ctx,
fetch(ctx, "example.com", "80", "/"),
[](std::exception_ptr ep) {
if (ep) {
try { std::rethrow_exception(ep); }
catch (const std::exception& e) {
std::println(stderr, "Error: {}", e.what());
}
}
});
ctx.run();
}La communication sécurisée est incontournable en production. Boost.Asio intègre un wrapper autour d'OpenSSL qui s'utilise comme un stream Asio classique :
#include <boost/asio/ssl.hpp>
namespace ssl = asio::ssl;awaitable<void> fetch_https(asio::io_context& ctx,
std::string host, std::string target) {
// Contexte SSL
ssl::context ssl_ctx(ssl::context::tlsv13_client);
ssl_ctx.set_default_verify_paths(); // Certificats CA du système
// Résolution
tcp::resolver resolver(ctx);
auto endpoints = co_await resolver.async_resolve(host, "443", use_awaitable);
// Stream SSL wrappant un socket TCP
ssl::stream<tcp::socket> stream(ctx, ssl_ctx);
// SNI (Server Name Indication) — nécessaire pour la plupart des serveurs
SSL_set_tlsext_host_name(stream.native_handle(), host.c_str());
// Connexion TCP
co_await asio::async_connect(
stream.next_layer(), endpoints, use_awaitable);
// Handshake TLS
co_await stream.async_handshake(ssl::stream_base::client, use_awaitable);
// À partir d'ici, le stream chiffré s'utilise comme un socket normal
http::request<http::empty_body> req{http::verb::get, target, 11};
req.set(http::field::host, host);
req.set(http::field::user_agent, "Beast-Client/1.0");
co_await http::async_write(stream, req, use_awaitable);
beast::flat_buffer buffer;
http::response<http::string_body> res;
co_await http::async_read(stream, buffer, res, use_awaitable);
std::println("HTTPS {} — {} octets",
static_cast<int>(res.result()), res.body().size());
// Shutdown TLS propre
beast::error_code ec;
co_await stream.async_shutdown(asio::redirect_error(use_awaitable, ec));
}Le design clé de Beast est que toutes ses fonctions (http::async_read, http::async_write, websocket::stream) acceptent n'importe quel type de stream compatible avec le concept AsyncReadStream / AsyncWriteStream d'Asio :
Beast HTTP/WebSocket
│
│ fonctionne sur n'importe quel stream :
│
├── tcp::socket (TCP brut)
├── ssl::stream<tcp::socket> (TLS/SSL)
├── votre_stream_custom (compression, logging, ...)
│
└── Le même code Beast fonctionne sur tous
C'est cette composabilité qui rend Beast puissant : vous pouvez ajouter TLS sans modifier votre code HTTP, simplement en changeant le type du stream.
Pour un serveur HTTPS, vous fournissez un certificat et une clé privée :
ssl::context ssl_ctx(ssl::context::tlsv13);
ssl_ctx.use_certificate_chain_file("server.pem");
ssl_ctx.use_private_key_file("server.key", ssl::context::pem);
// Le listener accepte des sockets TCP, les wrappe dans ssl::stream
awaitable<void> handle_https_session(
ssl::stream<tcp::socket> stream) {
// Handshake TLS côté serveur
co_await stream.async_handshake(
ssl::stream_base::server, use_awaitable);
// Utiliser stream exactement comme un socket TCP
beast::flat_buffer buffer;
http::request<http::string_body> req;
co_await http::async_read(stream, buffer, req, use_awaitable);
// ... traiter la requête ...
}💡 En développement, générez un certificat auto-signé avec :
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.pem -days 365 -nodes. En production, utilisez Let's Encrypt ou un certificat de votre CA d'entreprise.
Beast gère le protocole WebSocket nativement, incluant le handshake d'upgrade HTTP → WebSocket et le framing des messages :
#include <boost/beast/websocket.hpp>
namespace ws = beast::websocket;
awaitable<void> handle_websocket(tcp::socket socket) {
// Upgrade HTTP → WebSocket
ws::stream<tcp::socket> wss(std::move(socket));
co_await wss.async_accept(use_awaitable);
std::println("WebSocket connecté");
try {
beast::flat_buffer buffer;
while (true) {
// Lire un message WebSocket complet
co_await wss.async_read(buffer, use_awaitable);
// Echo : renvoyer le message
wss.text(wss.got_text()); // Préserver le type (texte ou binaire)
co_await wss.async_write(buffer.data(), use_awaitable);
buffer.consume(buffer.size()); // Vider le buffer
}
} catch (const boost::system::system_error& e) {
if (e.code() != ws::error::closed) {
std::println(stderr, "WebSocket error: {}", e.what());
}
}
std::println("WebSocket déconnecté");
}awaitable<void> websocket_client(asio::io_context& ctx,
std::string host, std::string port) {
tcp::resolver resolver(ctx);
auto endpoints = co_await resolver.async_resolve(host, port, use_awaitable);
ws::stream<tcp::socket> wss(ctx);
co_await asio::async_connect(wss.next_layer(), endpoints, use_awaitable);
// Handshake WebSocket
co_await wss.async_handshake(host, "/ws", use_awaitable);
// Envoyer un message
co_await wss.async_write(
asio::buffer(std::string("Hello WebSocket!")), use_awaitable);
// Recevoir la réponse
beast::flat_buffer buffer;
co_await wss.async_read(buffer, use_awaitable);
std::println("Reçu: {}", beast::buffers_to_string(buffer.data()));
// Fermeture propre
co_await wss.async_close(ws::close_code::normal, use_awaitable);
}Comme pour HTTP, il suffit de changer le type du stream sous-jacent :
// WebSocket non sécurisé
ws::stream<tcp::socket> wss(ctx);
// WebSocket sécurisé (WSS)
ws::stream<ssl::stream<tcp::socket>> wss(ctx, ssl_ctx);
// Le handshake TLS se fait d'abord sur la couche inférieure
co_await wss.next_layer().async_handshake(
ssl::stream_base::client, use_awaitable);
// Puis le handshake WebSocket
co_await wss.async_handshake(host, "/ws", use_awaitable);Pour les API REST, il est courant de manipuler du JSON dans les corps de requête/réponse. Boost.JSON s'intègre naturellement :
#include <boost/json.hpp>
namespace json = boost::json;
http::response<http::string_body>
handle_api(const http::request<http::string_body>& req) {
http::response<http::string_body> res;
res.version(req.version());
res.set(http::field::content_type, "application/json");
if (req.method() == http::verb::get && req.target() == "/api/users") {
// Construire du JSON
json::object users_response;
json::array users;
users.push_back({{"id", 1}, {"name", "Alice"}});
users.push_back({{"id", 2}, {"name", "Bob"}});
users_response["users"] = std::move(users);
users_response["total"] = 2;
res.result(http::status::ok);
res.body() = json::serialize(users_response);
}
else if (req.method() == http::verb::post && req.target() == "/api/users") {
// Parser le body JSON de la requête
try {
auto body = json::parse(req.body());
auto& obj = body.as_object();
auto name = obj.at("name").as_string();
json::object created;
created["id"] = 3;
created["name"] = name;
created["created"] = true;
res.result(http::status::created);
res.body() = json::serialize(created);
} catch (const std::exception& e) {
res.result(http::status::bad_request);
json::object err;
err["error"] = e.what();
res.body() = json::serialize(err);
}
}
res.prepare_payload();
return res;
}💡 Boost.JSON est une alternative à
nlohmann/json(chapitre 24) avec un focus différent : Boost.JSON privilégie la performance (moins d'allocations, parsing incrémental), tandis que nlohmann/json privilégie l'ergonomie (conversion automatique depuis/vers les types C++). Les deux sont de bons choix ; Boost.JSON s'intègre simplement mieux si vous utilisez déjà Boost.
Pour faciliter la migration ou le travail sur des projets utilisant l'une ou l'autre variante :
// Standalone Asio Boost.Asio
// ──────────────── ──────────
asio::io_context boost::asio::io_context
asio::ip::tcp boost::asio::ip::tcp
asio::steady_timer boost::asio::steady_timer
asio::awaitable<T> boost::asio::awaitable<T>
asio::use_awaitable boost::asio::use_awaitable
asio::co_spawn boost::asio::co_spawn
asio::detached boost::asio::detached
std::error_code boost::system::error_code
std::system_error boost::system::system_error // Standalone Boost
// ────────── ─────
#include <asio.hpp> #include <boost/asio.hpp>
#include <asio/co_spawn.hpp> #include <boost/asio/co_spawn.hpp>
#include <asio/ssl.hpp> #include <boost/asio/ssl.hpp>
#include <asio/use_awaitable.hpp> #include <boost/asio/use_awaitable.hpp>
#include <boost/beast.hpp> // Beast (Boost only)
#include <boost/json.hpp> // JSON (Boost only)Si vous voulez écrire du code compatible avec les deux, un header d'abstraction minimal suffit :
// asio_compat.hpp
#pragma once
#ifdef USE_BOOST_ASIO
#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/use_awaitable.hpp>
namespace net = boost::asio;
using error_code = boost::system::error_code;
#else
#include <asio.hpp>
#include <asio/co_spawn.hpp>
#include <asio/use_awaitable.hpp>
namespace net = asio;
using error_code = std::error_code;
#endif
using net::ip::tcp;
using net::ip::udp;
using net::awaitable;
using net::use_awaitable; Ce pattern d'alias de namespace (namespace net = ...) est largement utilisé dans l'écosystème C++ pour isoler le choix entre standalone et Boost.
Pour un serveur HTTP réaliste, la structure s'organise typiquement ainsi :
main.cpp
│
├── io_context + signal handling + thread pool
│
└── listener (acceptor)
│
└── pour chaque connexion :
│
├── detect_ssl() // Détecter HTTP vs HTTPS
│ ├── plain_session // HTTP
│ └── ssl_session // HTTPS
│
└── session (coroutine) :
│
├── async_read (requête)
├── router(req) → handler
│ ├── GET /api/... → api_handler
│ ├── POST /api/... → api_handler
│ ├── GET /static/... → file_handler
│ └── * → not_found
├── async_write (réponse)
└── keep_alive ? → boucle
Beast fournit http::file_body pour servir des fichiers directement du disque vers le socket, sans charger le fichier entier en mémoire :
http::response<http::file_body>
serve_file(const std::string& doc_root, std::string_view target) {
// Construire le chemin du fichier
std::string path = doc_root + std::string(target);
if (target.back() == '/') path += "index.html";
// Ouvrir le fichier
beast::error_code ec;
http::file_body::value_type body;
body.open(path.c_str(), beast::file_mode::scan, ec);
if (ec == beast::errc::no_such_file_or_directory) {
// 404
http::response<http::string_body> res{http::status::not_found, 11};
res.set(http::field::content_type, "text/plain");
res.body() = "File not found";
res.prepare_payload();
return {}; // Simplifié — en pratique, utilisez un variant
}
auto const size = body.size();
http::response<http::file_body> res{
std::piecewise_construct,
std::make_tuple(std::move(body)),
std::make_tuple(http::status::ok, 11)
};
res.set(http::field::content_type, "text/html"); // Déduire du MIME type
res.content_length(size);
return res;
}Boost 1.84 a introduit Boost.Cobalt, une librairie de coroutines qui simplifie certains patterns récurrents avec les awaitables natifs d'Asio :
#include <boost/cobalt.hpp>
namespace cobalt = boost::cobalt;
// Cobalt fournit son propre main coroutine
cobalt::main co_main(int argc, char* argv[]) {
// Pas besoin de créer un io_context explicitement
tcp::acceptor acceptor({co_await cobalt::this_coro::executor,
{tcp::v6(), 8080}});
while (true) {
auto socket = co_await acceptor.async_accept(cobalt::use_op);
cobalt::spawn(handle_client(std::move(socket)), cobalt::detached);
}
co_return 0;
}Cobalt réduit le boilerplate (pas de io_context explicite, pas de co_spawn dans main), mais c'est une addition récente encore en maturation. Pour les projets qui démarrent en 2026, surveiller son évolution est recommandé, mais les awaitables Asio natifs restent le choix éprouvé.
Choisissez Boost.Asio (plutôt que Standalone Asio) quand :
- Vous avez besoin de HTTP ou WebSocket — Beast n'existe que dans Boost.
- Vous avez besoin de TLS/SSL — Bien que Standalone Asio supporte SSL via un wrapper séparé, l'intégration Boost est plus mature et mieux documentée.
- Votre projet utilise déjà Boost — Ajouter Boost.Asio est gratuit si Boost est déjà une dépendance.
- Vous avez besoin de Boost.JSON, Boost.URL, ou d'autres librairies Boost complémentaires.
Ce point est développé plus en détail dans la section suivante (22.4.3).
Boost.Asio étend Standalone Asio avec un écosystème de librairies qui couvrent les besoins réels d'un serveur réseau moderne :
- Boost.Beast — HTTP/1.1 et WebSocket intégrés, avec un design de streams composables qui permet d'ajouter TLS sans modifier le code applicatif.
- Boost.Asio SSL — Wrapper OpenSSL natif pour HTTPS et WSS.
- Boost.JSON — Sérialisation JSON performante, intégrée naturellement avec les corps HTTP de Beast.
- Le même noyau Asio — Coroutines, callbacks, timers, résolution DNS : tout ce que vous avez appris en section 22.4.1 s'applique identiquement.
- Surcoût — La dépendance Boost est le seul prix. Pour les projets qui n'ont besoin que de TCP/UDP brut, Standalone Asio reste plus léger.
Pour les serveurs HTTP et les applications web en C++, la combinaison Boost.Asio + Beast + JSON est en 2026 la plateforme la plus mature et la plus complète de l'écosystème.
Prochaine étape → Section 22.4.3 : Quand choisir Standalone Asio vs Boost.Asio — critères de décision et matrice de choix.