...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Interfaces for transacting messages are structured into layers. The highest layer provides ease of use, while lower layers provide additional control and flexibility. The layers are arranged thusly:
Level |
Read/Write What |
Description |
---|---|---|
2 |
message |
At the top layer, these functions allow for an entire message to
be sent or received. They are designed for ease of use: |
1 |
partial |
These read functions enable partial message data to be received
into a DynamicBuffer.
They can be configured to perform bounded work: |
0 |
partial |
At the lowest level these read and write functions enable partial
message data to be transacted using a constant or mutable buffer
sequence: |
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:
// This DynamicBuffer will hold the received message multi_buffer buffer; // Read a complete message into the buffer's input area ws.read(buffer); // Set text mode if the received message was also text, // otherwise binary mode will be set. ws.text(ws.got_text()); // Echo the received message back to the peer. If the received // message was in text mode, the echoed message will also be // in text mode, otherwise it will be in binary mode. ws.write(buffer.data()); // Discard all of the bytes stored in the dynamic buffer, // otherwise the next call to read will append to the existing // data instead of building a fresh message. buffer.consume(buffer.size());
Important | |
---|---|
|
Some use-cases make it impractical or impossible to buffer the entire message ahead of time:
For these cases, the partial data oriented interface may be used. This example reads and echoes a complete message using this interface:
// This DynamicBuffer will hold the received message multi_buffer buffer; // Read the next message in pieces do { // Append up to 512 bytes of the message into the buffer ws.read_some(buffer, 512); } while(! ws.is_message_done()); // At this point we have a complete message in the buffer, now echo it // The echoed message will be sent in binary mode if the received // message was in binary mode, otherwise we will send in text mode. ws.binary(ws.got_binary()); // This buffer adapter allows us to iterate through buffer in pieces buffers_suffix<multi_buffer::const_buffers_type> cb{buffer.data()}; // Echo the received message in pieces. // This will cause the message to be broken up into multiple frames. for(;;) { using boost::asio::buffer_size; if(buffer_size(cb) > 512) { // There are more than 512 bytes left to send, just // send the next 512 bytes. The value `false` informs // the stream that the message is not complete. ws.write_some(false, buffers_prefix(512, cb)); // This efficiently discards data from the adapter by // simply ignoring it, but does not actually affect the // underlying dynamic buffer. cb.consume(512); } else { // Only 512 bytes or less remain, so write the whole // thing and inform the stream that this piece represents // the end of the message by passing `true`. ws.write_some(true, cb); break; } } // Discard all of the bytes stored in the dynamic buffer, // otherwise the next call to read will append to the existing // data instead of building a fresh message.