Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Send and Receive Messages

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: read, write, async_read, and async_write.

1

partial

These read functions enable partial message data to be received into a DynamicBuffer. They can be configured to perform bounded work: read_some, and async_read_some.

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: read_some, write_some, async_read_some, and async_write_some.

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] Important

websocket::stream is not thread-safe. Calls to stream member functions must all be made from the same implicit or explicit strand.

Frames

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.

PrevUpHomeNext