All Projects → test-scenarios → boost_beast_websocket_echo

test-scenarios / boost_beast_websocket_echo

Licence: BSL-1.0 License
A collection of Demo applications to try to help you understand how Asio and Beast work

Programming Languages

C++
36643 projects - #6 most used programming language
CMake
9771 projects

Projects that are alternatives of or similar to boost beast websocket echo

foxy
Session-based Beast/Asio wrapper requiring C++14
Stars: ✭ 61 (+408.33%)
Mutual labels:  beast, asio
Asio samples
Examples (code samples) describing the construction of active objects on the top of Boost.Asio. A code-based guide for client/server creation with usage of active object pattern by means of Boost C++ Libraries.
Stars: ✭ 191 (+1491.67%)
Mutual labels:  boost, asio
Cpp Bredis
Boost::ASIO low-level redis client (connector)
Stars: ✭ 117 (+875%)
Mutual labels:  boost, asio
Quantum
Powerful multi-threaded coroutine dispatcher and parallel execution engine
Stars: ✭ 291 (+2325%)
Mutual labels:  boost, coroutines
modern async cpp example
Just some example code from a lecture about modern async C++
Stars: ✭ 34 (+183.33%)
Mutual labels:  coroutines, asio
Cpp Rotor
Event loop friendly C++ actor micro-framework
Stars: ✭ 111 (+825%)
Mutual labels:  boost, asio
Webcc
Lightweight C++ HTTP client and server library based on Asio for embedding purpose.
Stars: ✭ 167 (+1291.67%)
Mutual labels:  boost, asio
Restc Cpp
Modern C++ REST Client library
Stars: ✭ 371 (+2991.67%)
Mutual labels:  coroutines, asio
SierraChartZorroPlugin
A Zorro broker API plugin for Sierra Chart, written in Win32 C++.
Stars: ✭ 22 (+83.33%)
Mutual labels:  boost, asio
Autobahn Cpp
WAMP for C++ in Boost/Asio
Stars: ✭ 231 (+1825%)
Mutual labels:  boost, asio
Beast
HTTP and WebSocket built on Boost.Asio in C++11
Stars: ✭ 3,241 (+26908.33%)
Mutual labels:  boost, asio
ufw
A minimalist framework for rapid server side applications prototyping in C++ with dependency injection support.
Stars: ✭ 19 (+58.33%)
Mutual labels:  boost, asio
asio-extensions
Additional functionality built on top of (Boost.)Asio
Stars: ✭ 16 (+33.33%)
Mutual labels:  boost, asio
malloy
A C++ library providing embeddable server & client components for both HTTP and WebSocket.
Stars: ✭ 29 (+141.67%)
Mutual labels:  boost, beast
mtxclient
Client API library for Matrix, built on top of Boost.Asio
Stars: ✭ 21 (+75%)
Mutual labels:  boost, asio
Boost Asio Study
Examples and toturials for C++ Boost Asio library.
Stars: ✭ 144 (+1100%)
Mutual labels:  boost, asio
act
ASIO Cooperative Task for await-based coroutine
Stars: ✭ 16 (+33.33%)
Mutual labels:  coroutines, asio
Beasthttp
Provides helper tools for creating RESTful services using Boost.Beast
Stars: ✭ 227 (+1791.67%)
Mutual labels:  boost, asio
boost-wintls
Native Windows TLS stream wrapper for use with boost::asio
Stars: ✭ 24 (+100%)
Mutual labels:  boost, asio
texugo
🦡 Fast, flexible, multiplatform, lightweight and, dependency-free message gateway
Stars: ✭ 18 (+50%)
Mutual labels:  boost, asio

Copyright (c) 2020 Richard Hodges ([email protected])

Disributed under the Boost Software Licence, Version 1.0. (See accompanying file LICENCE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt)

This repo contains a number of examples that I have written in response to being asked questions about Boost.Beast and Boost.Asio.

This repository makes NO CLAIMS WHATSOEVER to represent the views of the Beast team, or the views of Beast's author. In particular, the coding style used here is my own, totally arbitrary and is not endorsed by the Beast team or used in the Beast library itself.

I use a .clang-format file and take what I am given - because I can't be bothered to have code style arguments with other people or myself. My personal view is that the ends justify the means. Correct code is code that works, delivers required functionality, does not leak resources, shuts down cleanly and never segfaults. If you want to fork the repo, prettify the code and tinker with it for performance gains, please fill your boots.

I may even use the occasional goto. If that's going to make you spit your dummy¹ out, you can leave the room now.

The examples are provided in response to questions that do not already have a clear answer in the official documentation, or perhaps the documentation does cover it but people want to see an example because their level of pre-existing expertise is not sufficient to understand what it being presented to them in the documentation (this is common).

The Beast documentation clearly states that users of Beast should already be proficient with Asio.

This is reasonable as it keeps the human cost of support in the Beast maintenance team low.

However in reality, there is a chicken and egg scenario: Beast provides people a reason to use Asio. But it demands that people first learn Asio. Asio is hard to learn, even harder without a motivating use case. In today's HTTP/Websocket-centric world, Beast is often the motivating factor.

Asio is hard to learn because its documentation expects a certain level of knowledge in computer science, asynchronous messaging and underlying network IO. I've been using Asio for some time in production projects. My first attempts were wrong, because I didn't understand (and didn't bother to read up properly) on the meanings of Execution Context, Implicit Strand, Completion Handler and Completion Token. I just wanted to dive in and make things happen.

I expect since you're here, you do too.

This is why this repository exists. To provide annotated examples which hopefully explain why things are done the way they are (at least by me).

Common Misconceptions

Completion Handler

Many people think that Completion Handler means callback.

This is totally false.

OK, it's not totally false. A Completion Handler is a kind of callback, in the same way that an elephant is a kind of animal. All elephants are indeed animals, but not all animals have elongated trunks, long memories and are being hunted to extinction by psychopathic hairless apes for their solidified whiskers.

It is better to think of a Completion Handler, in the context of the last argument provided to an Asynchronous Initiating Function as a form of Future². It is a guarantee that some code will execute in the future by being Invoked by an Executor. Once. This means something different to merely Callback.

The code will be invoked on its Associated Executor, which is either:

  • The default executor associated with the IO object that has promised to invoke your completion handler, or
  • The executor you bound to your completion handler function with asio::bind_executor, or
  • The executor associated with the current coroutine (the one you specified in the call to asio::co_spawn if you supplied the asio::use_awaitable Completion Token, or
  • Totally irrelevant if you supplied the Completion Token asio::use_future

Completion Token

You might be tempted to think that a Completion Token means the same thing as a Completion Handler.

You would be wrong.

And this time, no qualification required, wrong.

A Completion Token is different from a Completion Handler in the same way that a high performance nickel-based superalloy jet turbine blade is different from a pet rat.

A Completion Handler is a function object that will be Invoked on the Associated Executor in response to the completion of an Asynchronous Operation.

A Completion Token describes to Asio how to rewrite the Initiating Function in order to:

  • Ensure that the Asynchronous Operation is initiated correctly.
  • Ensure that the correct Completion Handler is manufactured and passed to the asynchronous operation for later execution.
  • Ensure that the correct Result Type is returned to the caller, so that the caller can await the Completion of the Asynchronous Operation in the appropriate way.

For example, the Completion Handler produced by the Completion Token asio::use_awaitable, actually invokes code to resume the current coroutine upon operation completion.

The Completion Handler produced by the Completion Token asio::use_future actually invokes code to supply a value or an error to the std::promise backing the std::future you received as a result of calling the Initiating Function, e.g.:

    auto f = my_timer.async_wait(asio::use_future);

"But I've always just supplied a lambda"...

Right. Because if you supply an Completion Handler where a Completion Token is specified in the documents, a minimal transformation on your handler is made, to ensure that:

  • The initiating function returns void
  • All steps of the Asynchronous Operation make progress by being invoked by the IO object's Default Executor.
  • the supplied lambda will be Invoked through the io object's Default excecutor - once.

Examples:

using namespace asio;

// This is an execution context, not an executor. The only thing you should ever do 
// with this is call run() on it. Do not move it, store it in a shared pointer or
// any other daft thing. create it in main() and leave it there.

auto ioc = io_context();

// This is an Executor. You can copy this and pass it around. It is essentially
// a cheap handle to the io_context. 

auto e = ioc.get_executor();
   
// This timer's Default Executor is e
auto t = system_timer(e);

// as is the socket's
auto s = ip::tcp::socket(e);

// f is a std::future<void>. It will throw if the timer errors out or is cancelled.
auto f = t.async_wait(use_future);

// Returns an awaitable, which you must co_await. When it _resumes_ your coroutine,
// either `bytes` will contain the number of bytes you actually read or an exception
// (of type std::system_error) will be thrown.

std::vector mem(128);
auto bytes = co_await s.async_read_some(buffer(mem), use_awaitable);

async_wait(t, [](error_code ec){
    // This is a Completion Handler. Think of it as a FUTURE. It will happen exactly once.
    // It will happen by being called by executor e.
});

"All steps of the Asynchronous Operation make progress by being invoked by the IO object's Default Executor"

Wait, what?

consider:

auto bytes = co_await beast::http::async_read(sock, buffer, parser, asio::use_awaitable);

In the above asynchronous operation, how many actual asynchronous reads will be performed before the macro operation of consuming an entire HTTP request completes? It's not knowable until the final completion handler is invoked and (in this case), the current coroutine resumes execution.

So what?

Well, every time the sub-operations of which this operation is composed need to acquire or parse more data, they are going to touch the socket, or the buffer, or the parser, or all three.

On which thread should they do this?

They will make progress within the context of the Completion Handler's Associated Executor. In this case, the same executor on which the calling coroutine make progress.

Why is that important?

Because the 'reader' coroutine may not be the only one touching the socket. There may well be a 'writer' coroutine in progress too. And maybe a 'timer' coroutine which at some point might call cancel on our socket. Asio (and Beast) IO Objects are not thread-safe by design. That call to cancel or async_write had better happen on the same implicit or explicit strand of execution.

The reasons they are not thread safe are:

  1. Thread safety would unduly pessimise single-threaded programs or programs that create Implicit Strands. (i.e. they would be slower).

  2. IO Objects and in particular complex asynchronous streams and operations like the above function would be harder to maintain.

  3. Intermediate Completion Handlers would need to each maintain some kind of mutex before touching any data they had access to. (see 1 and 2)

In simple terms, it means that we can protect the entire "connection object" or "connection state" with a single strand and never have to worry about mutexes ever again.

Asio is able to achieve extremely high jitterless throughput because of its reliance of execution of code within executors. This method of execution provides very high confidence of fair sharing of cpu time. This is particularly important in high capacity server environments, but is also useful nice to have in clients.

Contributing

You are welcome to contribute a PR. If it brings new dependencies, please make sure they build properly using CMake FetchContent. User experience of this repo should be as trouble-free as possible.

Commenting and Raising Issues

Please feel free to comment and raise issues or contact me in slack once the procedure below has been followed.

Issues raised over code layout or style issues that do not affect program correctness or efficiency will simply be ignored and closed.

Getting Help

Here is the procedure for getting help:

Does the thing I'm doing resemble in any way one of the examples here?

if yes,

    copy the example and tinker from there.

    if you get really stuck, goto Slack

if no,

    submit a PR with a link to compilable code on Goldbolt demonstrating what you have already tried.

End

Link to Slack Invitation Join the #beast channel and politely as a question there. Expect to be flamed if the answer is in the docs.

Social Responsibility Policy

I'm not a socially reponsible person. Why I'm allowed out by myself it is a mystery to me. If you feel insulted or triggered by anything I say to you, you should probably grow up and stop being a baby. Even better, think of a more offensive retort. If you make me laugh, I may even respect you.

In code, there is correct (program functions) and there is wrong (program does not function, or functions by luck). Your feelings are irrelevant.

Environmental Responsibility Policy

If you are cruel to animals or engaged in activities that reduce the natural beauty of the World, then we probably won't get on. I'd keep that quiet if I were you, or better yet, change your ways and become a good person.

Footnotes

¹ Americans might use the word 'comforter' here...

² Anyone out there who can find an official text on computer science that disproves my claim that a completion handler is a form of future, please step forward. I enjoy a good argument, particularly one that is well presented and supported by factual documentation.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].