123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- [/
- Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
- Distributed under the Boost Software License, Version 1.0. (See accompanying
- file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- Official repository: https://github.com/boostorg/beast
- ]
- [section Comparison to Zaphoyd Studios WebSocket++]
- [variablelist
- [[
- How does this compare to [@https://www.zaphoyd.com/websocketpp websocketpp],
- an alternate header-only WebSocket implementation?
- ][
- [variablelist
- [[1. Synchronous Interface][
- Beast offers full support for WebSockets using a synchronous interface. It
- uses the same style of interfaces found in Boost.Asio: versions that throw
- exceptions, or versions that return the error code in a reference parameter:
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
- [websocketpp]
- ][
- [```
- template<class DynamicBuffer>
- void
- read(DynamicBuffer& dynabuf)
- ```]
- [
- /<not available>/
- ]
- ]]]]
- [[2. Connection Model][
- websocketpp supports multiple transports by utilizing a trait, the `config::transport_type`
- ([@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/connection.hpp#L60 asio transport example])
- To get an idea of the complexity involved with implementing a transport,
- compare the asio transport to the
- [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59 `iostream` transport]
- (a layer that allows websocket communication over a `std::iostream`).
- In contrast, Beast abstracts the transport by defining just one [*`NextLayer`]
- template argument The type requirements for [*`NextLayer`] are
- already familiar to users as they are documented in Asio:
- __AsyncReadStream__, __AsyncWriteStream__, __SyncReadStream__, __SyncWriteStream__.
- The type requirements for instantiating `beast::websocket::stream` versus
- `websocketpp::connection` with user defined types are vastly reduced
- (18 functions versus 2). Note that websocketpp connections are passed by
- `shared_ptr`. Beast does not use `shared_ptr` anywhere in its public interface.
- A `beast::websocket::stream` is constructible and movable in a manner identical
- to a `boost::asio::ip::tcp::socket`. Callers can put such objects in a
- `shared_ptr` if they want to, but there is no requirement to do so.
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp Beast]]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L234 websocketpp]]
- ][
- [```
- template<class NextLayer>
- class stream
- {
- NextLayer next_layer_;
- ...
- }
- ```]
- [```
- template <typename config>
- class connection
- : public config::transport_type::transport_con_type
- , public config::connection_base
- {
- public:
- typedef lib::shared_ptr<type> ptr;
- ...
- }
- ```]
- ]]]]
- [[3. Client and Server Role][
- websocketpp provides multi-role support through a hierarchy of
- different classes. A `beast::websocket::stream` is role-agnostic, it
- offers member functions to perform both client and server handshakes
- in the same class. The same types are used for client and server
- streams.
- [table
- [
- [Beast]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/server_endpoint.hpp#L39 websocketpp],
- [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/client_endpoint.hpp#L42 also]]
- ][
- [
- /<not needed>/
- ]
- [```
- template <typename config>
- class client : public endpoint<connection<config>,config>;
- template <typename config>
- class server : public endpoint<connection<config>,config>;
- ```]
- ]]]]
- [[4. Thread Safety][
- websocketpp uses mutexes to protect shared data from concurrent
- access. In contrast, Beast does not use mutexes anywhere in its
- implementation. Instead, it follows the Asio pattern. Calls to
- asynchronous initiation functions use the same method to invoke
- intermediate handlers as the method used to invoke the final handler,
- through the __asio_handler_invoke__ mechanism.
- The only requirement in Beast is that calls to asynchronous initiation
- functions are made from the same implicit or explicit strand. For
- example, if the `io_context` associated with a `beast::websocket::stream`
- is single threaded, this counts as an implicit strand and no performance
- costs associated with mutexes are incurred.
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/read_frame_op.ipp#L118 Beast]]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L706 websocketpp]]
- ][
- [```
- template <class Function>
- friend
- void asio_handler_invoke(Function&& f, read_frame_op* op)
- {
- return boost_asio_handler_invoke_helpers::invoke(f, op->d_->h);
- }
- ```]
- [```
- mutex_type m_read_mutex;
- ```]
- ]]]]
- [[5. Callback Model][
- websocketpp requires a one-time call to set the handler for each event
- in its interface (for example, upon message receipt). The handler is
- represented by a `std::function` equivalent. Its important to recognize
- that the websocketpp interface performs type-erasure on this handler.
- In comparison, Beast handlers are specified in a manner identical to
- Boost.Asio. They are function objects which can be copied or moved but
- most importantly they are not type erased. The compiler can see
- through the type directly to the implementation, permitting
- optimization. Furthermore, Beast follows the Asio rules for treatment
- of handlers. It respects any allocation, continuation, or invocation
- customizations associated with the handler through the use of argument
- dependent lookup overloads of functions such as `asio_handler_allocate`.
- The Beast completion handler is provided at the call site. For each
- call to an asynchronous initiation function, it is guaranteed that
- there will be exactly one final call to the handler. This functions
- exactly the same way as the asynchronous initiation functions found in
- Boost.Asio, allowing the composition of higher level abstractions.
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L834 Beast]]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L281 websocketpp],
- [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L473 also]]
- ][
- [```
- template<
- class DynamicBuffer, // Supports user defined types
- class ReadHandler // Handler is NOT type-erased
- >
- typename async_completion< // Return value customization
- ReadHandler, // supports futures and coroutines
- void(error_code)
- >::result_type
- async_read(
- DynamicBuffer& dynabuf,
- ReadHandler&& handler);
- ```]
- [```
- typedef lib::function<
- void(connection_hdl,message_ptr)
- > message_handler;
- void set_message_handler(message_handler h);
- ```]
- ]]]]
- [[6. Extensible Asynchronous Model][
- Beast fully supports the
- [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf Extensible Asynchronous Model]
- developed by Christopher Kohlhoff, author of Boost.Asio (see Section 8).
- Beast websocket asynchronous interfaces may be used seamlessly with
- `std::future` stackful/stackless coroutines, or user defined customizations.
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/stream.ipp#L378 Beast]]
- [websocketpp]
- ][
- [```
- beast::async_completion<
- ReadHandler,
- void(error_code)> completion{handler};
- read_op<
- DynamicBuffer, decltype(completion.handler)>{
- completion.handler, *this, op, buffer};
- return completion.result.get(); // Customization point
- ```]
- [
- /<not available>/
- ]
- ]]]]
- [[7. Message Buffering][
- websocketpp defines a message buffer, passed in arguments by
- `shared_ptr`, and an associated message manager which permits
- aggregation and reuse of memory. The implementation of
- `websocketpp::message` uses a `std::string` to hold the payload. If an
- incoming message is broken up into multiple frames, the string may be
- reallocated for each continuation frame. The `std::string` always uses
- the standard allocator, it is not possible to customize the choice of
- allocator.
- Beast allows callers to specify the object for receiving the message
- or frame data, which is of any type meeting the requirements of
- __DynamicBuffer__ (modeled after `boost::asio::streambuf`).
- Beast comes with the class __basic_multi_buffer__, an efficient
- implementation of the __DynamicBuffer__ concept which makes use of multiple
- allocated octet arrays. If an incoming message is broken up into
- multiple pieces, no reallocation occurs. Instead, new allocations are
- appended to the sequence when existing allocations are filled. Beast
- does not impose any particular memory management model on callers. The
- __basic_multi_buffer__ provided by beast supports standard allocators through
- a template argument. Use the __DynamicBuffer__ that comes with beast,
- customize the allocator if you desire, or provide your own type that
- meets the requirements.
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/message_buffer/message.hpp#L78 websocketpp]]
- ][
- [```
- template<class DynamicBuffer>
- read(DynamicBuffer& dynabuf);
- ```]
- [```
- template <template<class> class con_msg_manager>
- class message {
- public:
- typedef lib::shared_ptr<message> ptr;
- ...
- std::string m_payload;
- ...
- };
- ```]
- ]]]]
- [[8. Sending Messages][
- When sending a message, websocketpp requires that the payload is
- packaged in a `websocketpp::message` object using `std::string` as the
- storage, or it requires a copy of the caller provided buffer by
- constructing a new message object. Messages are placed onto an
- outgoing queue. An asynchronous write operation runs in the background
- to clear the queue. No user facing handler can be registered to be
- notified when messages or frames have completed sending.
- Beast doesn't allocate or make copies of buffers when sending data. The
- caller's buffers are sent in-place. You can use any object meeting the
- requirements of __ConstBufferSequence, permitting efficient scatter-gather I/O.
- The [*ConstBufferSequence] interface allows callers to send data from
- memory-mapped regions (not possible in websocketpp). Callers can also
- use the same buffers to send data to multiple streams, for example
- broadcasting common subscription data to many clients at once. For
- each call to `async_write` the completion handler is called once when
- the data finishes sending, in a manner identical to `boost::asio::async_write`.
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1048 Beast]]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L672 websocketpp]]
- ][
- [```
- template<class ConstBufferSequence>
- void
- write(ConstBufferSequence const& buffers);
- ```]
- [```
- lib::error_code send(std::string const & payload,
- frame::opcode::value op = frame::opcode::text);
- ...
- lib::error_code send(message_ptr msg);
- ```]
- ]]]]
- [[9. Streaming Messages][
- websocketpp requires that the entire message fit into memory, and that
- the size is known ahead of time.
- Beast allows callers to compose messages in individual frames. This is
- useful when the size of the data is not known ahead of time or if it
- is not desired to buffer the entire message in memory at once before
- sending it. For example, sending periodic output of a database query
- running on a coroutine. Or sending the contents of a file in pieces,
- without bringing it all into memory.
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1151 Beast]]
- [websocketpp]
- ][
- [```
- template<class ConstBufferSequence>
- void
- write_some(bool fin,
- ConstBufferSequence const& buffers);
- ```]
- [
- /<not available>/
- ]
- ]]]]
- [[10. Flow Control][
- The websocketpp read implementation continuously reads asynchronously
- from the network and buffers message data. To prevent unbounded growth
- and leverage TCP/IP's flow control mechanism, callers can periodically
- turn this 'read pump' off and back on.
- In contrast a `beast::websocket::stream` does not independently begin
- background activity, nor does it buffer messages. It receives data only
- when there is a call to an asynchronous initiation function (for
- example `beast::websocket::stream::async_read`) with an associated handler.
- Applications do not need to implement explicit logic to regulate the
- flow of data. Instead, they follow the traditional model of issuing a
- read, receiving a read completion, processing the message, then
- issuing a new read and repeating the process.
- [table
- [
- [Beast]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L728 websocketpp]]
- ][
- [
- /<implicit>/
- ]
- [```
- lib::error_code pause_reading();
- lib::error_code resume_reading();
- ```]
- ]]]]
- [[11. Connection Establishment][
- websocketpp offers the `endpoint` class which can handle binding and
- listening to a port, and spawning connection objects.
- Beast does not reinvent the wheel here, callers use the interfaces
- already in `boost::asio` for receiving incoming connections resolving
- host names, or establishing outgoing connections. After the socket (or
- `boost::asio::ssl::stream`) is connected, the `beast::websocket::stream`
- is constructed around it and the WebSocket handshake can be performed.
- Beast users are free to implement their own "connection manager", but
- there is no requirement to do so.
- [table
- [
- [[@http://www.boost.org/doc/html/boost_asio/reference/async_connect.html Beast],
- [@http://www.boost.org/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept.html also]]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/endpoint.hpp#L52 websocketpp]]
- ][
- [```
- #include <boost/asio.hpp>
- ```]
- [```
- template <typename config>
- class endpoint : public config::socket_type;
- ```]
- ]]]]
- [[12. WebSocket Handshaking][
- Callers invoke `beast::websocket::accept` to perform the WebSocket
- handshake, but there is no requirement to use this function. Advanced
- users can perform the WebSocket handshake themselves. Beast WebSocket
- provides the tools for composing the request or response, and the
- Beast HTTP interface provides the container and algorithms for sending
- and receiving HTTP/1 messages including the necessary HTTP Upgrade
- request for establishing the WebSocket session.
- Beast allows the caller to pass the incoming HTTP Upgrade request for
- the cases where the caller has already received an HTTP message.
- This flexibility permits novel and robust implementations. For example,
- a listening socket that can handshake in multiple protocols on the
- same port.
- Sometimes callers want to read some bytes on the socket before reading
- the WebSocket HTTP Upgrade request. Beast allows these already-received
- bytes to be supplied to an overload of the accepting function to permit
- sophisticated features. For example, a listening socket that can
- accept both regular WebSocket and Secure WebSocket (SSL) connections.
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L501 Beast],
- [@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L401 also]]
- [websocketpp]
- ][
- [```
- template<class ConstBufferSequence>
- void
- accept(ConstBufferSequence const& buffers);
- template<class Allocator>
- void
- accept(http::header<true, http::basic_fields<Allocator>> const& req);
- ```]
- [
- /<not available>/
- ]
- ]]]]
- ]
- ]]
- ]
- [endsect]
|