2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
7 // Official repository: https://github.com/boostorg/beast
10 //------------------------------------------------------------------------------
12 // Example: WebSocket server, fast
14 //------------------------------------------------------------------------------
16 /* This server contains the following ports:
18 Synchronous <base port + 0>
19 Asynchronous <base port + 1>
20 Coroutine <base port + 2>
22 This program is optimized for the Autobahn|Testsuite
23 benchmarking and WebSocket compliants testing program.
26 https://github.com/crossbario/autobahn-testsuite
29 #include <boost/beast/core.hpp>
30 #include <boost/beast/http.hpp>
31 #include <boost/beast/version.hpp>
32 #include <boost/beast/websocket.hpp>
33 #include <boost/asio/spawn.hpp>
34 #include <boost/asio/strand.hpp>
44 namespace beast
= boost::beast
; // from <boost/beast.hpp>
45 namespace http
= beast::http
; // from <boost/beast/http.hpp>
46 namespace websocket
= beast::websocket
; // from <boost/beast/websocket.hpp>
47 namespace net
= boost::asio
; // from <boost/asio.hpp>
48 using tcp
= boost::asio::ip::tcp
; // from <boost/asio/ip/tcp.hpp>
50 //------------------------------------------------------------------------------
54 fail(beast::error_code ec
, char const* what
)
56 std::cerr
<< (std::string(what
) + ": " + ec
.message() + "\n");
59 // Adjust settings on the stream
60 template<class NextLayer
>
62 setup_stream(websocket::stream
<NextLayer
>& ws
)
64 // These values are tuned for Autobahn|Testsuite, and
65 // should also be generally helpful for increased performance.
67 websocket::permessage_deflate pmd
;
68 pmd
.client_enable
= true;
69 pmd
.server_enable
= true;
73 ws
.auto_fragment(false);
75 // Autobahn|Testsuite needs this
76 ws
.read_message_max(64 * 1024 * 1024);
79 //------------------------------------------------------------------------------
82 do_sync_session(websocket::stream
<beast::tcp_stream
>& ws
)
88 // Set a decorator to change the Server of the handshake
89 ws
.set_option(websocket::stream_base::decorator(
90 [](websocket::response_type
& res
)
92 res
.set(http::field::server
, std::string(
93 BOOST_BEAST_VERSION_STRING
) + "-Sync");
98 return fail(ec
, "accept");
102 beast::flat_buffer buffer
;
105 if(ec
== websocket::error::closed
)
108 return fail(ec
, "read");
109 ws
.text(ws
.got_text());
110 ws
.write(buffer
.data(), ec
);
112 return fail(ec
, "write");
118 net::io_context
& ioc
,
119 tcp::endpoint endpoint
)
121 beast::error_code ec
;
122 tcp::acceptor acceptor
{ioc
, endpoint
};
125 tcp::socket socket
{ioc
};
127 acceptor
.accept(socket
, ec
);
129 return fail(ec
, "accept");
131 std::thread(std::bind(
133 websocket::stream
<beast::tcp_stream
>(
134 std::move(socket
)))).detach();
138 //------------------------------------------------------------------------------
140 // Echoes back all received WebSocket messages
141 class async_session
: public std::enable_shared_from_this
<async_session
>
143 websocket::stream
<beast::tcp_stream
> ws_
;
144 beast::flat_buffer buffer_
;
147 // Take ownership of the socket
149 async_session(tcp::socket
&& socket
)
150 : ws_(std::move(socket
))
155 // Start the asynchronous operation
159 // Set suggested timeout settings for the websocket
161 websocket::stream_base::timeout::suggested(
162 beast::role_type::server
));
164 // Set a decorator to change the Server of the handshake
165 ws_
.set_option(websocket::stream_base::decorator(
166 [](websocket::response_type
& res
)
168 res
.set(http::field::server
, std::string(
169 BOOST_BEAST_VERSION_STRING
) + "-Async");
172 // Accept the websocket handshake
174 beast::bind_front_handler(
175 &async_session::on_accept
,
176 shared_from_this()));
180 on_accept(beast::error_code ec
)
183 return fail(ec
, "accept");
192 // Read a message into our buffer
195 beast::bind_front_handler(
196 &async_session::on_read
,
197 shared_from_this()));
202 beast::error_code ec
,
203 std::size_t bytes_transferred
)
205 boost::ignore_unused(bytes_transferred
);
207 // This indicates that the async_session was closed
208 if(ec
== websocket::error::closed
)
215 ws_
.text(ws_
.got_text());
218 beast::bind_front_handler(
219 &async_session::on_write
,
220 shared_from_this()));
225 beast::error_code ec
,
226 std::size_t bytes_transferred
)
228 boost::ignore_unused(bytes_transferred
);
231 return fail(ec
, "write");
234 buffer_
.consume(buffer_
.size());
241 // Accepts incoming connections and launches the sessions
242 class async_listener
: public std::enable_shared_from_this
<async_listener
>
244 net::io_context
& ioc_
;
245 tcp::acceptor acceptor_
;
249 net::io_context
& ioc
,
250 tcp::endpoint endpoint
)
252 , acceptor_(net::make_strand(ioc
))
254 beast::error_code ec
;
257 acceptor_
.open(endpoint
.protocol(), ec
);
264 // Allow address reuse
265 acceptor_
.set_option(net::socket_base::reuse_address(true), ec
);
268 fail(ec
, "set_option");
272 // Bind to the server address
273 acceptor_
.bind(endpoint
, ec
);
280 // Start listening for connections
282 net::socket_base::max_listen_connections
, ec
);
290 // Start accepting incoming connections
301 // The new connection gets its own strand
302 acceptor_
.async_accept(
303 net::make_strand(ioc_
),
304 beast::bind_front_handler(
305 &async_listener::on_accept
,
306 shared_from_this()));
310 on_accept(beast::error_code ec
, tcp::socket socket
)
318 // Create the async_session and run it
319 std::make_shared
<async_session
>(std::move(socket
))->run();
322 // Accept another connection
327 //------------------------------------------------------------------------------
331 websocket::stream
<beast::tcp_stream
>& ws
,
332 net::yield_context yield
)
334 beast::error_code ec
;
338 // Set suggested timeout settings for the websocket
340 websocket::stream_base::timeout::suggested(
341 beast::role_type::server
));
343 // Set a decorator to change the Server of the handshake
344 ws
.set_option(websocket::stream_base::decorator(
345 [](websocket::response_type
& res
)
347 res
.set(http::field::server
, std::string(
348 BOOST_BEAST_VERSION_STRING
) + "-Fiber");
351 ws
.async_accept(yield
[ec
]);
353 return fail(ec
, "accept");
357 beast::flat_buffer buffer
;
359 ws
.async_read(buffer
, yield
[ec
]);
360 if(ec
== websocket::error::closed
)
363 return fail(ec
, "read");
365 ws
.text(ws
.got_text());
366 ws
.async_write(buffer
.data(), yield
[ec
]);
368 return fail(ec
, "write");
374 net::io_context
& ioc
,
375 tcp::endpoint endpoint
,
376 net::yield_context yield
)
378 beast::error_code ec
;
380 tcp::acceptor
acceptor(ioc
);
381 acceptor
.open(endpoint
.protocol(), ec
);
383 return fail(ec
, "open");
385 acceptor
.set_option(net::socket_base::reuse_address(true), ec
);
387 return fail(ec
, "set_option");
389 acceptor
.bind(endpoint
, ec
);
391 return fail(ec
, "bind");
393 acceptor
.listen(net::socket_base::max_listen_connections
, ec
);
395 return fail(ec
, "listen");
399 tcp::socket
socket(ioc
);
401 acceptor
.async_accept(socket
, yield
[ec
]);
409 acceptor
.get_executor(),
413 beast::tcp_stream
>(std::move(socket
)),
414 std::placeholders::_1
));
418 //------------------------------------------------------------------------------
420 int main(int argc
, char* argv
[])
422 // Check command line arguments.
426 "Usage: websocket-server-fast <address> <starting-port> <threads>\n" <<
428 " websocket-server-fast 0.0.0.0 8080 1\n"
430 " starting-port+0 for synchronous,\n"
431 " starting-port+1 for asynchronous,\n"
432 " starting-port+2 for coroutine.\n";
435 auto const address
= net::ip::make_address(argv
[1]);
436 auto const port
= static_cast<unsigned short>(std::atoi(argv
[2]));
437 auto const threads
= std::max
<int>(1, std::atoi(argv
[3]));
439 // The io_context is required for all I/O
440 net::io_context ioc
{threads
};
443 std::thread(beast::bind_front_handler(
448 static_cast<unsigned short>(port
+ 0u)}
452 std::make_shared
<async_listener
>(
456 static_cast<unsigned short>(port
+ 1u)})->run();
459 boost::asio::spawn(ioc
,
465 static_cast<unsigned short>(port
+ 2u)},
466 std::placeholders::_1
));
468 // Run the I/O service on the requested number of threads
469 std::vector
<std::thread
> v
;
470 v
.reserve(threads
- 1);
471 for(auto i
= threads
- 1; i
> 0; --i
)