1//
2// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7// Official repository: https://github.com/boostorg/beast
8//
9
10//------------------------------------------------------------------------------
11//
12// Example: WebSocket server, coroutine
13//
14//------------------------------------------------------------------------------
15
16#include <boost/beast/core.hpp>
17#include <boost/beast/websocket.hpp>
18#include <boost/asio/awaitable.hpp>
19#include <boost/asio/co_spawn.hpp>
20#include <boost/asio/detached.hpp>
21#include <boost/asio/use_awaitable.hpp>
22#include <algorithm>
23#include <cstdlib>
24#include <functional>
25#include <iostream>
26#include <memory>
27#include <string>
28#include <thread>
29#include <vector>
30
31#if defined(BOOST_ASIO_HAS_CO_AWAIT)
32
33namespace beast = boost::beast; // from <boost/beast.hpp>
34namespace http = beast::http; // from <boost/beast/http.hpp>
35namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
36namespace net = boost::asio; // from <boost/asio.hpp>
37using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
38
39using stream = websocket::stream<
40 typename beast::tcp_stream::rebind_executor<
41 typename net::use_awaitable_t<>::executor_with_default<net::any_io_executor>>::other>;
42
43//------------------------------------------------------------------------------
44
45// Echoes back all received WebSocket messages
46net::awaitable<void>
47do_session(stream ws)
48{
49 // Set suggested timeout settings for the websocket
50 ws.set_option(
51 websocket::stream_base::timeout::suggested(
52 beast::role_type::server));
53
54 // Set a decorator to change the Server of the handshake
55 ws.set_option(websocket::stream_base::decorator(
56 [](websocket::response_type& res)
57 {
58 res.set(http::field::server,
59 std::string(BOOST_BEAST_VERSION_STRING) +
60 " websocket-server-coro");
61 }));
62
63 // Accept the websocket handshake
64 co_await ws.async_accept();
65
66 try
67 {
68 for(;;)
69 {
70 // This buffer will hold the incoming message
71 beast::flat_buffer buffer;
72
73 // Read a message
74 co_await ws.async_read(buffer);
75
76 // Echo the message back
77 ws.text(ws.got_text());
78 co_await ws.async_write(buffer.data());
79 }
80 }
81 catch(const boost::system::system_error & se)
82 {
83 if (se.code() != websocket::error::closed)
84 throw;
85 }
86}
87
88//------------------------------------------------------------------------------
89
90// Accepts incoming connections and launches the sessions
91net::awaitable<void>
92do_listen(
93 tcp::endpoint endpoint)
94{
95
96 // Open the acceptor
97 auto acceptor = net::use_awaitable.as_default_on(tcp::acceptor(co_await net::this_coro::executor));
98 acceptor.open(endpoint.protocol());
99
100 // Allow address reuse
101 acceptor.set_option(net::socket_base::reuse_address(true));
102
103 // Bind to the server address
104 acceptor.bind(endpoint);
105
106 // Start listening for connections
107 acceptor.listen(net::socket_base::max_listen_connections);
108
109 for(;;)
110 boost::asio::co_spawn(
111 acceptor.get_executor(),
112 do_session(stream(co_await acceptor.async_accept())),
113 [](std::exception_ptr e)
114 {
115 if (e)
116 {
117 try
118 {
119 std::rethrow_exception(e);
120 }
121 catch (std::exception &e) {
122 std::cerr << "Error in session: " << e.what() << "\n";
123 }
124 }
125 });
126}
127
128int main(int argc, char* argv[])
129{
130 // Check command line arguments.
131 if (argc != 4)
132 {
133 std::cerr <<
134 "Usage: websocket-server-awaitable <address> <port> <threads>\n" <<
135 "Example:\n" <<
136 " websocket-server-awaitable 0.0.0.0 8080 1\n";
137 return EXIT_FAILURE;
138 }
139 auto const address = net::ip::make_address(argv[1]);
140 auto const port = static_cast<unsigned short>(std::atoi(argv[2]));
141 auto const threads = std::max<int>(1, std::atoi(argv[3]));
142
143 // The io_context is required for all I/O
144 net::io_context ioc(threads);
145
146 // Spawn a listening port
147 boost::asio::co_spawn(
148 ioc,
149 do_listen(tcp::endpoint{address, port}),
150 [](std::exception_ptr e)
151 {
152 if (e)
153 try
154 {
155 std::rethrow_exception(e);
156 }
157 catch(std::exception & e)
158 {
159 std::cerr << "Error: " << e.what() << "\n";
160 }
161 });
162
163 // Run the I/O service on the requested number of threads
164 std::vector<std::thread> v;
165 v.reserve(threads - 1);
166 for(auto i = threads - 1; i > 0; --i)
167 v.emplace_back(
168 [&ioc]
169 {
170 ioc.run();
171 });
172 ioc.run();
173
174 return EXIT_SUCCESS;
175}
176
177#else
178
179int main(int, char * [])
180{
181 std::printf(format: "awaitables require C++20\n");
182 return 1;
183}
184
185#endif

source code of boost/libs/beast/example/websocket/server/awaitable/websocket_server_awaitable.cpp