forked from boostorg/beast
652 lines
23 KiB
Plaintext
652 lines
23 KiB
Plaintext
[/
|
|
Copyright (c) 2013-2017 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)
|
|
]
|
|
|
|
[section:websocket WebSocket]
|
|
|
|
[block '''
|
|
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
|
|
<member><link linkend="beast.websocket.creation">Creation</link></member>
|
|
<member><link linkend="beast.websocket.connections">Making connections</link></member>
|
|
<member><link linkend="beast.websocket.handshaking">Handshaking</link></member>
|
|
<member><link linkend="beast.websocket.accepting">Accepting</link></member>
|
|
<member><link linkend="beast.websocket.messages">Messages</link></member>
|
|
<member><link linkend="beast.websocket.control">Control Frames</link></member>
|
|
<member><link linkend="beast.websocket.notes">Notes</link></member>
|
|
</simplelist></entry></row></tbody></tgroup></informaltable>
|
|
''']
|
|
|
|
The WebSocket Protocol enables two-way communication between a client
|
|
running untrusted code in a controlled environment to a remote host that has
|
|
opted-in to communications from that code. The protocol consists of an opening
|
|
handshake followed by basic message framing, layered over TCP. The goal of
|
|
this technology is to provide a mechanism for browser-based applications that
|
|
need two-way communication with servers that does not rely on opening multiple
|
|
HTTP connections.
|
|
|
|
Beast provides developers with a robust WebSocket implementation built on
|
|
Boost.Asio with a consistent asynchronous model using a modern C++ approach.
|
|
|
|
The WebSocket protocol is described fully in
|
|
[@https://tools.ietf.org/html/rfc6455 rfc6455]
|
|
|
|
[note
|
|
The following documentation assumes familiarity with both
|
|
Boost.Asio and the WebSocket protocol specification described in __rfc6455__.
|
|
]
|
|
|
|
|
|
|
|
|
|
[section:creation Creation]
|
|
|
|
The interface to Beast's WebSocket implementation is a single template
|
|
class [link beast.ref.websocket__stream `websocket::stream`] which
|
|
wraps a "next layer" object. The next layer object must meet the requirements
|
|
of [link beast.ref.streams.SyncStream [*SyncStream]] if synchronous
|
|
operations are performed, or
|
|
[link beast.ref.streams.AsyncStream [*AsyncStream]] if asynchronous
|
|
operations are performed, or both. Arguments supplied during construction are
|
|
passed to next layer's constructor. Here we declare a websocket stream over
|
|
a TCP/IP socket with ownership of the socket:
|
|
```
|
|
boost::asio::io_service ios;
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
```
|
|
|
|
[heading Using SSL]
|
|
|
|
To use WebSockets over SSL, choose an SSL stream for the next layer template
|
|
argument when constructing the stream.
|
|
```
|
|
#include <beast/websocket/ssl.hpp>
|
|
#include <beast/websocket.hpp>
|
|
#include <boost/asio/ssl.hpp>
|
|
|
|
boost::asio::io_service ios;
|
|
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
|
|
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ws{ios, ctx};
|
|
```
|
|
|
|
[note
|
|
When creating websocket stream objects using SSL, it is necessary
|
|
to include the file `<beast/websocket/ssl.hpp>`.
|
|
]
|
|
|
|
[heading Non-owning references]
|
|
|
|
For servers that can handshake in multiple protocols, it may be desired
|
|
to wrap an object that already exists. This socket can be moved in:
|
|
```
|
|
boost::asio::ip::tcp::socket sock;
|
|
...
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{std::move(sock)};
|
|
```
|
|
|
|
Or, the wrapper can be constructed with a non-owning reference. In
|
|
this case, the caller is responsible for managing the lifetime of the
|
|
underlying socket being wrapped:
|
|
```
|
|
boost::asio::ip::tcp::socket sock;
|
|
...
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
|
```
|
|
|
|
The layer being wrapped can be accessed through the websocket's "next layer",
|
|
permitting callers to interact directly with its interface.
|
|
```
|
|
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
|
|
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws{ios, ctx};
|
|
...
|
|
ws.next_layer().shutdown(); // ssl::stream shutdown
|
|
```
|
|
|
|
[warning
|
|
Initiating read and write operations on the next layer while
|
|
stream operations are being performed can break invariants, and
|
|
result in undefined behavior.
|
|
]
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:connections Making connections]
|
|
|
|
Connections are established by using the interfaces which already exist
|
|
for the next layer. For example, making an outgoing connection:
|
|
```
|
|
std::string const host = "mywebapp.com";
|
|
boost::asio::io_service ios;
|
|
boost::asio::ip::tcp::resolver r{ios};
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
boost::asio::connect(ws.next_layer(),
|
|
r.resolve(boost::asio::ip::tcp::resolver::query{host, "ws"}));
|
|
```
|
|
|
|
Accepting an incoming connection:
|
|
```
|
|
void do_accept(boost::asio::ip::tcp::acceptor& acceptor)
|
|
{
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{acceptor.get_io_service()};
|
|
acceptor.accept(ws.next_layer());
|
|
}
|
|
```
|
|
|
|
When using SSL, which itself wraps a next layer object that is usualy a
|
|
TCP/IP socket, multiple calls to retrieve the next layer may be required:
|
|
```
|
|
beast::websocket::stream<boost::asio::ssl::stream<
|
|
boost::asio::ip::tcp::socket>> ws{ios, ctx};
|
|
|
|
// connect the underlying TCP/IP socket
|
|
ws.next_layer().next_layer().connect(ep);
|
|
|
|
// perform SSL handshake
|
|
ws.next_layer().handshake(boost::asio::ssl::stream_base::client);
|
|
|
|
// perform WebSocket handshake
|
|
ws.handshake("localhost", "/");
|
|
```
|
|
|
|
[note
|
|
Examples use synchronous interfaces for clarity of exposition.
|
|
]
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:handshaking Handshaking]
|
|
|
|
A WebSocket session begins when one side sends the HTTP Upgrade request
|
|
for websocket, and the other side sends an appropriate HTTP response
|
|
indicating that the request was accepted and that the connection has
|
|
been upgraded. The HTTP Upgrade request must include the Host HTTP field,
|
|
and the target of the resource to request. The stream member functions
|
|
[link beast.ref.websocket__stream.handshake `handshake`] and
|
|
[link beast.ref.websocket__stream.async_handshake `async_handshake`]
|
|
are used to send the request with the required host and target strings.
|
|
```
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
...
|
|
ws.set_option(beast::websocket::keep_alive(true));
|
|
ws.handshake("localhost", "/");
|
|
```
|
|
|
|
The implementation will create and send an HTTP request that typically
|
|
looks like this:
|
|
|
|
[table WebSocket Upgrade HTTP Request
|
|
[[Serialized Octets][Description]]
|
|
[[
|
|
```
|
|
GET / HTTP/1.1
|
|
Host: localhost
|
|
Upgrade: websocket
|
|
Connection: upgrade
|
|
Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA==
|
|
Sec-WebSocket-Version: 13
|
|
User-Agent: Beast
|
|
```
|
|
][
|
|
The host and target parameters become part of the Host field
|
|
and request-target in the resulting HTTP request. The key is
|
|
generated by the implementation. Callers may add fields or
|
|
modify fields by providing a ['decorator], described below.
|
|
]]]
|
|
|
|
[heading Decorators]
|
|
|
|
If the caller wishes to add or modify fields, the member functions
|
|
[link beast.ref.websocket__stream.handshake_ex `handshake_ex`] and
|
|
[link beast.ref.websocket__stream.async_handshake_ex `async_handshake_ex`]
|
|
are provided which allow an additional function object, called a
|
|
['decorator], to be passed. The decorator is invoked to modify
|
|
the HTTP Upgrade request as needed. This example sets a subprotocol
|
|
on the request:
|
|
```
|
|
void decorate(beast::websocket::request_type& req)
|
|
{
|
|
req.fields.insert("Sec-WebSocket-Protocol", "xmpp;ws-chat");
|
|
}
|
|
...
|
|
ws.handshake_ex("localhost", "/", &decorate);
|
|
|
|
```
|
|
|
|
The HTTP Upgrade request produced by the previous call will look thusly:
|
|
|
|
[table Decorated WebSocket Upgrade HTTP Request
|
|
[[Serialized Octets][Description]]
|
|
[[
|
|
```
|
|
GET / HTTP/1.1
|
|
Host: localhost
|
|
Upgrade: websocket
|
|
Connection: upgrade
|
|
Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA==
|
|
Sec-WebSocket-Version: 13
|
|
Sec-WebSocket-Protocol: xmpp;ws-chat
|
|
User-Agent: Beast
|
|
```
|
|
][
|
|
Undefined behavior results if the decorator modifies the fields
|
|
specific to perform the WebSocket Upgrade , such as the Upgrade
|
|
and Connection fields.
|
|
]]]
|
|
|
|
[heading Filtering]
|
|
|
|
When a client receives an HTTP Upgrade response from the server indicating
|
|
a successful upgrade, the caller may wish to perform additional validation
|
|
on the received HTTP response message. For example, to check that the
|
|
response to a basic authentication challenge is valid. To achieve this,
|
|
overloads of the handshake member function allow the caller to store the
|
|
received HTTP message in an output reference argument as
|
|
[link beast.ref.websocket__response_type `response_type`]
|
|
as follows:
|
|
```
|
|
beast::websocket::response_type res;
|
|
ws.handshake(res, "localhost", "/");
|
|
if(! res.fields.exists("Sec-WebSocket-Protocol"))
|
|
throw std::invalid_argument("missing subprotocols");
|
|
```
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:accepting Accepting]
|
|
|
|
A [link beast.ref.websocket__stream `stream`] automatically
|
|
handles receiving and processing the HTTP response to the handshake request.
|
|
The call to handshake is successful if a HTTP response is received with the
|
|
101 "Switching Protocols" status code. On failure, an error is returned or an
|
|
exception is thrown. Depending on the keep alive setting, the socket may remain
|
|
open for a subsequent handshake attempt
|
|
|
|
Performing a handshake for an incoming websocket upgrade request operates
|
|
similarly. If the handshake fails, an error is returned or exception thrown:
|
|
```
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
...
|
|
ws.accept();
|
|
```
|
|
|
|
Successful WebSocket Upgrade responses generated by the implementation will
|
|
typically look like this:
|
|
|
|
[table Decorated WebSocket Upgrade HTTP Request
|
|
[[Serialized Octets][Description]]
|
|
[[
|
|
```
|
|
HTTP/1.1 101 Switching Protocols
|
|
Upgrade: websocket
|
|
Connection: upgrade
|
|
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
|
|
Server: Beast/40
|
|
```
|
|
][
|
|
The Sec-WebSocket-Accept field value is generated from the
|
|
request in a fashion specified by the WebSocket protocol.
|
|
]]]
|
|
|
|
[heading Decorators]
|
|
|
|
If the caller wishes to add or modify fields, the member functions
|
|
[link beast.ref.websocket__stream.accept_ex `accept_ex`] and
|
|
[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`]
|
|
are provided which allow an additional function object, called a
|
|
['decorator], to be passed. The decorator is invoked to modify
|
|
the HTTP Upgrade request as needed. This example sets the Server
|
|
field on the response:
|
|
```
|
|
ws.accept_ex(
|
|
[](beast::websocket::response_type& res)
|
|
{
|
|
res.fields.insert("Server", "AcmeServer");
|
|
});
|
|
|
|
```
|
|
|
|
The HTTP Upgrade response produced by the previous call will look thusly:
|
|
|
|
[table Decorated WebSocket Upgrade HTTP Request
|
|
[[Serialized Octets][Description]]
|
|
[[
|
|
```
|
|
HTTP/1.1 101 Switching Protocols
|
|
Upgrade: websocket
|
|
Connection: upgrade
|
|
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
|
|
Server: AcmeServer
|
|
```
|
|
][
|
|
When the Upgrade request fails, the implementation will still invoke
|
|
the decorator to modify the response. In this case, the response
|
|
object will have a status code other than 101.
|
|
|
|
Undefined behavior results when the upgrade request is successful
|
|
and the decorator modifies the fields specific to perform the
|
|
WebSocket Upgrade , such as the Upgrade and Connection fields.
|
|
]]]
|
|
|
|
[heading Passing HTTP Requests]
|
|
|
|
When implementing an HTTP server that also supports WebSocket, the
|
|
server usually reads the HTTP request from the client. To detect when
|
|
the incoming HTTP request is a WebSocket Upgrade request, the function
|
|
[link beast.ref.websocket__is_upgrade `is_upgrade`] may be used.
|
|
|
|
Once the caller determines that the HTTP request is a WebSocket Upgrade,
|
|
additional overloads of
|
|
[link beast.ref.websocket__stream.accept `accept`],
|
|
[link beast.ref.websocket__stream.accept_ex `accept_ex`],
|
|
[link beast.ref.websocket__stream.async_accept `async_accept`], and
|
|
[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`]
|
|
are provided which receive the entire HTTP request header as an object
|
|
to perform the handshake. In this example, the request is first read
|
|
in using the HTTP algorithms, and then passed to a newly constructed
|
|
stream:
|
|
```
|
|
void handle_connection(boost::asio::ip::tcp::socket& sock)
|
|
{
|
|
beast::flat_buffer buffer;
|
|
beast::http::request<beast::http::string_body> req;
|
|
beast::http::read(sock, buffer, req);
|
|
if(beast::websocket::is_upgrade(req))
|
|
{
|
|
beast::websocket::stream<decltype(sock)> ws{std::move(sock)};
|
|
ws.accept(req);
|
|
}
|
|
}
|
|
```
|
|
|
|
[heading Buffered Handshakes]
|
|
|
|
Sometimes a server implementation wishes to read octets on the stream
|
|
in order to route the incoming request. For example, a server may read
|
|
the first 6 octets after accepting an incoming connection to determine
|
|
if a TLS protocol is being negotiated, and choose a suitable implementation
|
|
at run-time. In the case where the server wishes to accept the incoming
|
|
request as an HTTP WebSocket Upgrade request, additional overloads of
|
|
[link beast.ref.websocket__stream.accept `accept`],
|
|
[link beast.ref.websocket__stream.accept_ex `accept_ex`],
|
|
[link beast.ref.websocket__stream.async_accept `async_accept`], and
|
|
[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`]
|
|
are provided which receive the additional buffered octects and consume
|
|
them as part of the handshake.
|
|
|
|
In this example, the server reads the initial HTTP message into the
|
|
specified dynamic buffer as an octet sequence in the buffer's output
|
|
area, and later uses those octets to attempt an HTTP WebSocket Upgrade:
|
|
```
|
|
void do_accept(boost::asio::ip::tcp::socket& sock)
|
|
{
|
|
boost::asio::streambuf sb;
|
|
boost::asio::read_until(sock, sb, "\r\n\r\n");
|
|
...
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
|
ws.accept(sb.data());
|
|
...
|
|
}
|
|
```
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:messages Messages]
|
|
|
|
After the WebSocket handshake is accomplished, callers may send and receive
|
|
messages using the message oriented interface. This interface requires that
|
|
all of the buffers representing the message are known ahead of time:
|
|
```
|
|
void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
|
|
{
|
|
beast::multi_buffer b;
|
|
beast::websocket::opcode::value op;
|
|
ws.read(op, b);
|
|
|
|
ws.set_option(beast::websocket::message_type{op});
|
|
ws.write(b.data());
|
|
sb.consume(b.size());
|
|
}
|
|
```
|
|
|
|
[important
|
|
Calls to [link beast.ref.websocket__stream.set_option `set_option`]
|
|
must be made from the same implicit or explicit strand as that used
|
|
to perform other operations.
|
|
]
|
|
|
|
[heading Frames]
|
|
|
|
Some use-cases make it impractical or impossible to buffer the entire
|
|
message ahead of time:
|
|
|
|
* Streaming multimedia to an endpoint.
|
|
* Sending a message that does not fit in memory at once.
|
|
* Providing incremental results as they become available.
|
|
|
|
For these cases, the frame oriented interface may be used. This
|
|
example reads and echoes a complete message using this interface:
|
|
```
|
|
void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
|
|
{
|
|
beast::multi_buffer b;
|
|
beast::websocket::frame_info fi;
|
|
for(;;)
|
|
{
|
|
ws.read_frame(fi, b);
|
|
if(fi.fin)
|
|
break;
|
|
}
|
|
ws.set_option(beast::websocket::message_type{fi.op});
|
|
beast::consuming_buffers<
|
|
beast::multi_buffer::const_buffers_type> cb{b.data()};
|
|
for(;;)
|
|
{
|
|
using boost::asio::buffer_size;
|
|
std::size_t size = std::min(buffer_size(cb));
|
|
if(size > 512)
|
|
{
|
|
ws.write_frame(false, beast::prepare_buffers(512, cb));
|
|
cb.consume(512);
|
|
}
|
|
else
|
|
{
|
|
ws.write_frame(true, cb);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:control Control Frames]
|
|
|
|
Control frames are small (less than 128 bytes) messages entirely contained
|
|
in an individual WebSocket frame. They may be sent at any time by either
|
|
peer on an established connection, and can appear in between continuation
|
|
frames for a message. There are three types of control frames: ping, pong,
|
|
and close.
|
|
|
|
A sent ping indicates a request that the sender wants to receive a pong. A
|
|
pong is a response to a ping. Pongs may be sent unsolicited, at any time.
|
|
One use for an unsolicited pong is to inform the remote peer that the
|
|
session is still active after a long period of inactivity. A close frame
|
|
indicates that the remote peer wishes to close the WebSocket connection.
|
|
The connection is considered gracefully closed when each side has sent
|
|
and received a close frame.
|
|
|
|
During read operations, Beast automatically reads and processes control
|
|
frames. Pings are replied to as soon as possible with a pong, received
|
|
ping and pongs are delivered to the ping callback. The receipt of a close
|
|
frame initiates the WebSocket close procedure, eventually resulting in the
|
|
error code [link beast.ref.websocket__error `error::closed`] being delivered
|
|
to the caller in a subsequent read operation, assuming no other error
|
|
takes place.
|
|
|
|
A consequence of this automatic behavior is that caller-initiated read
|
|
operations can cause socket writes. However, these writes will not
|
|
compete with caller-initiated write operations. For the purposes of
|
|
correctness with respect to the stream invariants, caller-initiated
|
|
read operations still only count as a read. This means that callers can
|
|
have a simultaneously active read, write, and ping operation in progress,
|
|
while the implementation also automatically handles control frames.
|
|
|
|
[heading Ping and Pong Frames]
|
|
|
|
Ping and pong messages are control frames which may be sent at any time
|
|
by either peer on an established WebSocket connection. They are sent
|
|
using the functions
|
|
[link beast.ref.websocket__stream.ping `ping`] and
|
|
[link beast.ref.websocket__stream.pong `pong`].
|
|
|
|
To be notified of ping and pong control frames, callers may register a
|
|
"ping callback" using [link beast.ref.websocket__stream.set_option `set_option`].
|
|
The object provided with this option should be callable with the following
|
|
signature:
|
|
```
|
|
void on_ping(bool is_pong, ping_data const& payload);
|
|
...
|
|
ws.set_option(ping_callback{&on_ping});
|
|
```
|
|
|
|
When a ping callback is registered, all pings and pongs received through
|
|
either synchronous read functions or asynchronous read functions will
|
|
invoke the ping callback, with the value of `is_pong` set to `true` if a
|
|
pong was received else `false` if a ping was received. The payload of
|
|
the ping or pong control frame is passed in the payload argument.
|
|
|
|
Unlike regular completion handlers used in calls to asynchronous initiation
|
|
functions, the ping callback only needs to be set once. The callback is not
|
|
reset when a ping or pong is received. The same callback is used for both
|
|
synchronous and asynchronous reads. The ping callback is passive; in order
|
|
to receive pings and pongs, a synchronous or asynchronous stream read
|
|
function must be active.
|
|
|
|
[note
|
|
When an asynchronous read function receives a ping or pong, the
|
|
ping callback is invoked in the same manner as that used to invoke
|
|
the final completion handler of the corresponding read function.
|
|
]
|
|
|
|
[heading Close Frames]
|
|
|
|
The WebSocket protocol defines a procedure and control message for initiating
|
|
a close of the session. Handling of close initiated by the remote end of the
|
|
connection is performed automatically. To manually initiate a close, use
|
|
the [link beast.ref.websocket__stream.close `close`] function:
|
|
```
|
|
ws.close();
|
|
```
|
|
|
|
When the remote peer initiates a close by sending a close frame, Beast
|
|
will handle it for you by causing the next read to return `error::closed`.
|
|
When this error code is delivered, it indicates to the application that
|
|
the WebSocket connection has been closed cleanly, and that the TCP/IP
|
|
connection has been closed. After initiating a close, it is necessary to
|
|
continue reading messages until receiving the error `error::closed`. This
|
|
is because the remote peer may still be sending message and control frames
|
|
before it receives and responds to the close frame.
|
|
|
|
[important
|
|
To receive the [link beast.ref.websocket__error `error::closed`]
|
|
error, a read operation is required.
|
|
]
|
|
|
|
[heading Auto-fragment]
|
|
|
|
To ensure timely delivery of control frames, large messages can be broken up
|
|
into smaller sized frames. The automatic fragment option turns on this
|
|
feature, and the write buffer size option determines the maximum size of
|
|
the fragments:
|
|
```
|
|
...
|
|
ws.set_option(beast::websocket::auto_fragment{true});
|
|
ws.set_option(beast::websocket::write_buffer_size{16384});
|
|
```
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:notes Notes]
|
|
|
|
Because calls to read data may return a variable amount of bytes, the
|
|
interface to calls that read data require an object that meets the requirements
|
|
of __DynamicBuffer__. This concept is modeled on
|
|
[@http://www.boost.org/doc/html/boost_asio/reference/basic_streambuf.html `boost::asio::basic_streambuf`].
|
|
|
|
The implementation does not perform queueing or buffering of messages. If
|
|
desired, these features should be provided by callers. The impact of this
|
|
design is that library users are in full control of the allocation strategy
|
|
used to store data and the back-pressure applied on the read and write side
|
|
of the underlying TCP/IP connection.
|
|
|
|
[heading Asynchronous Operations]
|
|
|
|
Asynchronous versions are available for all functions:
|
|
```
|
|
websocket::opcode op;
|
|
ws.async_read(op, sb,
|
|
[](boost::system::error_code const& ec)
|
|
{
|
|
...
|
|
});
|
|
```
|
|
|
|
Calls to asynchronous initiation functions support the extensible asynchronous
|
|
model developed by the Boost.Asio author, allowing for traditional completion
|
|
handlers, stackful or stackless coroutines, and even futures:
|
|
```
|
|
void echo(websocket::stream<ip::tcp::socket>& ws,
|
|
boost::asio::yield_context yield)
|
|
{
|
|
ws.async_read(sb, yield);
|
|
std::future<websocket::error_code> fut =
|
|
ws.async_write, sb.data(), boost::use_future);
|
|
...
|
|
}
|
|
```
|
|
|
|
[heading The io_service]
|
|
|
|
The creation and operation of the
|
|
[@http://www.boost.org/doc/html/boost_asio/reference/io_service.html `boost::asio::io_service`]
|
|
associated with the underlying stream is left to the callers, permitting any
|
|
implementation strategy including one that does not require threads for
|
|
environments where threads are unavailable. Beast WebSocket itself does not
|
|
use or require threads.
|
|
|
|
[heading Thread Safety]
|
|
|
|
Like a regular asio socket, a [link beast.ref.websocket__stream `stream`] is
|
|
not thread safe. Callers are responsible for synchronizing operations on the
|
|
socket using an implicit or explicit strand, as per the Asio documentation.
|
|
The asynchronous interface supports one active read and one active write
|
|
simultaneously. Undefined behavior results if two or more reads or two or
|
|
more writes are attempted concurrently. Caller initiated WebSocket ping, pong,
|
|
and close operations each count as an active write.
|
|
|
|
The implementation uses composed asynchronous operations internally; a high
|
|
level read can cause both reads and writes to take place on the underlying
|
|
stream. This behavior is transparent to callers.
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[endsect]
|
|
|
|
[include quickref.xml]
|