• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1[/
2    Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
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[section Comparison to Zaphoyd Studios WebSocket++]
11
12[variablelist
13
14[[
15    How does this compare to [@https://www.zaphoyd.com/websocketpp websocketpp],
16    an alternate header-only WebSocket implementation?
17][
18[variablelist
19
20[[1. Synchronous Interface][
21
22Beast offers full support for WebSockets using a synchronous interface. It
23uses the same style of interfaces found in Boost.Asio: versions that throw
24exceptions, or versions that return the error code in a reference parameter:
25
26[table
27    [
28        [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
29        [websocketpp]
30    ][
31        [```
32            template<class DynamicBuffer>
33            void
34            read(DynamicBuffer& dynabuf)
35        ```]
36        [
37            /<not available>/
38        ]
39]]]]
40
41[[2. Connection Model][
42
43websocketpp supports multiple transports by utilizing a trait, the `config::transport_type`
44([@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/connection.hpp#L60 asio transport example])
45To get an idea of the complexity involved with implementing a transport,
46compare the asio transport to the
47[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59 `iostream` transport]
48(a layer that allows websocket communication over a `std::iostream`).
49
50In contrast, Beast abstracts the transport by defining just one [*`NextLayer`]
51template argument The type requirements for [*`NextLayer`] are
52already familiar to users as they are documented in Asio:
53__AsyncReadStream__, __AsyncWriteStream__, __SyncReadStream__, __SyncWriteStream__.
54
55The type requirements for instantiating `beast::websocket::stream` versus
56`websocketpp::connection` with user defined types are vastly reduced
57(18 functions versus 2). Note that websocketpp connections are passed by
58`shared_ptr`. Beast does not use `shared_ptr` anywhere in its public interface.
59A `beast::websocket::stream` is constructible and movable in a manner identical
60to a `boost::asio::ip::tcp::socket`. Callers can put such objects in a
61`shared_ptr` if they want to, but there is no requirement to do so.
62
63[table
64    [
65        [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp Beast]]
66        [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L234 websocketpp]]
67    ][
68        [```
69            template<class NextLayer>
70            class stream
71            {
72                NextLayer next_layer_;
73                ...
74            }
75        ```]
76        [```
77            template <typename config>
78            class connection
79            : public config::transport_type::transport_con_type
80            , public config::connection_base
81            {
82            public:
83                typedef lib::shared_ptr<type> ptr;
84                ...
85            }
86        ```]
87]]]]
88
89[[3. Client and Server Role][
90
91websocketpp provides multi-role support through a hierarchy of
92different classes. A `beast::websocket::stream` is role-agnostic, it
93offers member functions to perform both client and server handshakes
94in the same class. The same types are used for client and server
95streams.
96
97[table
98    [
99        [Beast]
100        [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/server_endpoint.hpp#L39 websocketpp],
101            [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/client_endpoint.hpp#L42 also]]
102    ][
103        [
104            /<not needed>/
105        ]
106        [```
107            template <typename config>
108            class client : public endpoint<connection<config>,config>;
109            template <typename config>
110            class server : public endpoint<connection<config>,config>;
111        ```]
112]]]]
113
114[[4. Thread Safety][
115
116websocketpp uses mutexes to protect shared data from concurrent
117access. In contrast, Beast does not use mutexes anywhere in its
118implementation. Instead, it follows the Asio pattern. Calls to
119asynchronous initiation functions use the same method to invoke
120intermediate handlers as the method used to invoke the final handler,
121through the __asio_handler_invoke__ mechanism.
122
123The only requirement in Beast is that calls to asynchronous initiation
124functions are made from the same implicit or explicit strand. For
125example, if the `io_context` associated with a `beast::websocket::stream`
126is single threaded, this counts as an implicit strand and no performance
127costs associated with mutexes are incurred.
128
129[table
130    [
131        [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/read_frame_op.ipp#L118 Beast]]
132        [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L706 websocketpp]]
133    ][
134        [```
135            template <class Function>
136            friend
137            void asio_handler_invoke(Function&& f, read_frame_op* op)
138            {
139                return boost_asio_handler_invoke_helpers::invoke(f, op->d_->h);
140            }
141        ```]
142        [```
143            mutex_type m_read_mutex;
144        ```]
145]]]]
146
147[[5. Callback Model][
148
149websocketpp requires a one-time call to set the handler for each event
150in its interface (for example, upon message receipt). The handler is
151represented by a `std::function` equivalent. Its important to recognize
152that the websocketpp interface performs type-erasure on this handler.
153
154In comparison, Beast handlers are specified in a manner identical to
155Boost.Asio. They are function objects which can be copied or moved but
156most importantly they are not type erased. The compiler can see
157through the type directly to the implementation, permitting
158optimization. Furthermore, Beast follows the Asio rules for treatment
159of handlers. It respects any allocation, continuation, or invocation
160customizations associated with the handler through the use of argument
161dependent lookup overloads of functions such as `asio_handler_allocate`.
162
163The Beast completion handler is provided at the call site. For each
164call to an asynchronous initiation function, it is guaranteed that
165there will be exactly one final call to the handler. This functions
166exactly the same way as the asynchronous initiation functions found in
167Boost.Asio, allowing the composition of higher level abstractions.
168
169[table
170    [
171        [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L834 Beast]]
172        [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L281 websocketpp],
173            [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L473 also]]
174    ][
175        [```
176            template<
177                class DynamicBuffer,    // Supports user defined types
178                class ReadHandler       // Handler is NOT type-erased
179            >
180            typename async_completion<  // Return value customization
181                ReadHandler,            // supports futures and coroutines
182                void(error_code)
183                    >::result_type
184            async_read(
185                DynamicBuffer& dynabuf,
186                ReadHandler&& handler);
187        ```]
188        [```
189            typedef lib::function<
190                void(connection_hdl,message_ptr)
191                    > message_handler;
192            void set_message_handler(message_handler h);
193        ```]
194]]]]
195
196[[6. Extensible Asynchronous Model][
197
198Beast fully supports the
199[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf Extensible Asynchronous Model]
200developed by Christopher Kohlhoff, author of Boost.Asio (see Section 8).
201
202Beast websocket asynchronous interfaces may be used seamlessly with
203`std::future` stackful/stackless coroutines, or user defined customizations.
204
205[table
206    [
207        [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/stream.ipp#L378 Beast]]
208        [websocketpp]
209    ][
210        [```
211            beast::async_completion<
212                ReadHandler,
213                void(error_code)> completion{handler};
214            read_op<
215                DynamicBuffer, decltype(completion.handler)>{
216                    completion.handler, *this, op, buffer};
217
218            return completion.result.get();     // Customization point
219        ```]
220        [
221            /<not available>/
222        ]
223]]]]
224
225[[7. Message Buffering][
226
227websocketpp defines a message buffer, passed in arguments by
228`shared_ptr`, and an associated message manager which permits
229aggregation and reuse of memory. The implementation of
230`websocketpp::message` uses a `std::string` to hold the payload. If an
231incoming message is broken up into multiple frames, the string may be
232reallocated for each continuation frame. The `std::string` always uses
233the standard allocator, it is not possible to customize the choice of
234allocator.
235
236Beast allows callers to specify the object for receiving the message
237or frame data, which is of any type meeting the requirements of
238__DynamicBuffer__ (modeled after `boost::asio::streambuf`).
239
240Beast comes with the class __basic_multi_buffer__, an efficient
241implementation of the __DynamicBuffer__ concept which makes use of multiple
242allocated octet arrays. If an incoming message is broken up into
243multiple pieces, no reallocation occurs. Instead, new allocations are
244appended to the sequence when existing allocations are filled. Beast
245does not impose any particular memory management model on callers. The
246__basic_multi_buffer__ provided by beast supports standard allocators through
247a template argument. Use the __DynamicBuffer__ that comes with beast,
248customize the allocator if you desire, or provide your own type that
249meets the requirements.
250
251[table
252    [
253        [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
254        [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/message_buffer/message.hpp#L78 websocketpp]]
255    ][
256        [```
257            template<class DynamicBuffer>
258            read(DynamicBuffer& dynabuf);
259        ```]
260        [```
261            template <template<class> class con_msg_manager>
262            class message {
263            public:
264                typedef lib::shared_ptr<message> ptr;
265                ...
266                std::string m_payload;
267                ...
268            };
269        ```]
270]]]]
271
272[[8. Sending Messages][
273
274When sending a message, websocketpp requires that the payload is
275packaged in a `websocketpp::message` object using `std::string` as the
276storage, or it requires a copy of the caller provided buffer by
277constructing a new message object. Messages are placed onto an
278outgoing queue. An asynchronous write operation runs in the background
279to clear the queue. No user facing handler can be registered to be
280notified when messages or frames have completed sending.
281
282Beast doesn't allocate or make copies of buffers when sending data. The
283caller's buffers are sent in-place. You can use any object meeting the
284requirements of __ConstBufferSequence, permitting efficient scatter-gather I/O.
285
286The [*ConstBufferSequence] interface allows callers to send data from
287memory-mapped regions (not possible in websocketpp). Callers can also
288use the same buffers to send data to multiple streams, for example
289broadcasting common subscription data to many clients at once. For
290each call to `async_write` the completion handler is called once when
291the data finishes sending, in a manner identical to `boost::asio::async_write`.
292
293[table
294    [
295        [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1048 Beast]]
296        [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L672 websocketpp]]
297    ][
298        [```
299            template<class ConstBufferSequence>
300            void
301            write(ConstBufferSequence const& buffers);
302        ```]
303        [```
304            lib::error_code send(std::string const & payload,
305                frame::opcode::value op = frame::opcode::text);
306            ...
307            lib::error_code send(message_ptr msg);
308        ```]
309]]]]
310
311[[9. Streaming Messages][
312
313websocketpp requires that the entire message fit into memory, and that
314the size is known ahead of time.
315
316Beast allows callers to compose messages in individual frames. This is
317useful when the size of the data is not known ahead of time or if it
318is not desired to buffer the entire message in memory at once before
319sending it. For example, sending periodic output of a database query
320running on a coroutine. Or sending the contents of a file in pieces,
321without bringing it all into memory.
322
323[table
324    [
325        [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1151 Beast]]
326        [websocketpp]
327    ][
328        [```
329            template<class ConstBufferSequence>
330            void
331            write_some(bool fin,
332                ConstBufferSequence const& buffers);
333        ```]
334        [
335            /<not available>/
336        ]
337]]]]
338
339[[10. Flow Control][
340
341The websocketpp read implementation continuously reads asynchronously
342from the network and buffers message data. To prevent unbounded growth
343and leverage TCP/IP's flow control mechanism, callers can periodically
344turn this 'read pump' off and back on.
345
346In contrast a `beast::websocket::stream` does not independently begin
347background activity, nor does it buffer messages. It receives data only
348when there is a call to an asynchronous initiation function (for
349example `beast::websocket::stream::async_read`) with an associated handler.
350Applications do not need to implement explicit logic to regulate the
351flow of data. Instead, they follow the traditional model of issuing a
352read, receiving a read completion, processing the message, then
353issuing a new read and repeating the process.
354
355[table
356    [
357        [Beast]
358        [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L728 websocketpp]]
359    ][
360        [
361            /<implicit>/
362        ]
363        [```
364            lib::error_code pause_reading();
365            lib::error_code resume_reading();
366        ```]
367]]]]
368
369[[11. Connection Establishment][
370
371websocketpp offers the `endpoint` class which can handle binding and
372listening to a port, and spawning connection objects.
373
374Beast does not reinvent the wheel here, callers use the interfaces
375already in `boost::asio` for receiving incoming connections resolving
376host names, or establishing outgoing connections. After the socket (or
377`boost::asio::ssl::stream`) is connected, the `beast::websocket::stream`
378is constructed around it and the WebSocket handshake can be performed.
379
380Beast users are free to implement their own "connection manager", but
381there is no requirement to do so.
382
383[table
384    [
385        [[@http://www.boost.org/doc/html/boost_asio/reference/async_connect.html Beast],
386            [@http://www.boost.org/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept.html also]]
387        [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/endpoint.hpp#L52 websocketpp]]
388    ][
389        [```
390            #include <boost/asio.hpp>
391        ```]
392        [```
393            template <typename config>
394            class endpoint : public config::socket_type;
395        ```]
396]]]]
397
398[[12. WebSocket Handshaking][
399
400Callers invoke `beast::websocket::accept` to perform the WebSocket
401handshake, but there is no requirement to use this function. Advanced
402users can perform the WebSocket handshake themselves. Beast WebSocket
403provides the tools for composing the request or response, and the
404Beast HTTP interface provides the container and algorithms for sending
405and receiving HTTP/1 messages including the necessary HTTP Upgrade
406request for establishing the WebSocket session.
407
408Beast allows the caller to pass the incoming HTTP Upgrade request for
409the cases where the caller has already received an HTTP message.
410This flexibility permits novel and robust implementations. For example,
411a listening socket that can handshake in multiple protocols on the
412same port.
413
414Sometimes callers want to read some bytes on the socket before reading
415the WebSocket HTTP Upgrade request. Beast allows these already-received
416bytes to be supplied to an overload of the accepting function to permit
417sophisticated features. For example, a listening socket that can
418accept both regular WebSocket and Secure WebSocket (SSL) connections.
419
420[table
421    [
422        [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L501 Beast],
423            [@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L401 also]]
424        [websocketpp]
425    ][
426        [```
427            template<class ConstBufferSequence>
428            void
429            accept(ConstBufferSequence const& buffers);
430
431            template<class Allocator>
432            void
433            accept(http::header<true, http::basic_fields<Allocator>> const& req);
434        ```]
435        [
436            /<not available>/
437        ]
438]]]]
439
440]
441]]
442
443]
444
445[endsect]
446