Boost logo

Boost Users :

Subject: Re: [Boost-users] [iostreams] Devices and WOULD_BLOCK
From: David Hawkins (dwh_at_[hidden])
Date: 2015-01-22 02:52:24


Hi Gavin,

>> Yep, I ran a few tests where I ensured that two complete messages
>> were received into a boost::asio::streambuf and confirmed that
>> read_until '[' returned the SOP first index, read_until ']' returned
>> the first EOP index, I then consume()'d those characters and
>> repeated the call to read_until and confirmed it returned immediately
>> based on the streambuf contents. So this all works as described by
>> the documentation.
>
> Yep. Just be careful if you're mixing calls to read_until with calls to
> read -- read_until may fetch a larger amount of data into the streambuf
> (beyond the delimiter), while read will always wait for new data, even
> if the streambuf isn't empty. So you need to explicitly check the
> streambuf before calling read().
>
> Of course if you're only dealing with purely delimited data then this
> shouldn't be an issue, as you'll only be using [async_]read_until.

That is an interesting warning. I hadn't really thought about whether
I could mix the blocking and non-blocking read commands (but would
have assumed I should not!). The modified chat application uses
using only the async versions, i.e., async_read_until, and async_write.

>> Once I finish my variation on the chat client/server, I'd be happy
>> to post the code. At a minimum it would provide code for people on
>> the list to review/comment on, and any final version of the code
>> would benefit anyone interested in reading streams containing
>> a different style of packet than that used in the boost example
>> chat client/server.
>
> Asio has a dedicated mailing list
> (https://lists.sourceforge.net/lists/listinfo/asio-users), which I
> believe the library maintainer pays closer attention to than this list;
> it may be worthwhile asking there.

Oh, good point. My code started in Boost.Iostreams, but now its in
the Boost.Asio camp :)

> Maybe you could even get it included in the official docs. :)

Yes, that was my hope. You can never have too many examples!

> (I have some working streambuf code but it's not really in
> consumable-example form.)

Ok, so here's a streambuf question for you. In my attempt to
modify the chat server as much as possible, I initially modified
the buffering to use a streambuf member variable ... but that
fails, since a streambuf is non-copyable. My solution was to use
a shared_ptr<streambuf>> - see the chat_message.hpp code below.

Given that the shared_ptr is reference counted, when the server
'delivers' a read message to multiple clients, the shared pointer
reference count on a new message read from a client will increment
as its copied to each of the clients connected to a session, and
then decrement as each server client handler write pops the message
off its write queue.

At least I assume that is what is going on (I just finished
modifying the server and its working ok), I'll use the debugger
and trace the server code tomorrow to check.

Do you see anything wrong with using a shared_ptr<streambuf>
member variable? I wanted to use a streambuf so that I could
pass it directly to the async_read_until and async_write functions
(saving the buffer() conversions used in the original code).

Cheers,
Dave

//
// chat_message.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2014 Christopher M. Kohlhoff (chris at kohlhoff
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)
//
// Chat client/server message coder/decoder.
//
// This is based on the Boost chat client/server example modified
// to use a different encoding/decoding method.
//
// The encoded message to send or receive is stored in a
// std::shared_ptr<boost::asio::streambuf> buffer_
// a shared pointer is used, since streambufs are non-copyable.
//

#ifndef CHAT_MESSAGE_HPP
#define CHAT_MESSAGE_HPP

#include <boost/asio.hpp>
#include <iostream>
#include <string>

class chat_message
{
public:

        // Protocol codes
        //
        // * const char is used rather than an enum hack, so
        // that the data type is consistent for stream
        // insertion (otherwise a cast would be required)
        //
        // * the codes are public, so that async_read_until can be
        // used with chat_message::SOP and chat_message::EOP.
        //
        // * Note: g++ 4.8.3 does not support inline initialization,
        // so the values are assigned below.
        //
        // * Encoding/decoding and the use of the escape mask
        //
        // --------------------------------------
        // | Protocol code || Masked Code |
        // |---------------------||-------|-------|
        // | Name | ASCII | code || ASCII | code |
        // |------|-------|------||-------| ------|
        // | SOP | '[' | 0x5B || '{' | 0x7B |
        // | EOP | ']' | 0x5D || '}' | 0x7D |
        // | ESC | '\' | 0x5C || '|' | 0x7C |
        // --------------------------------------
        //
        // The XOR mask of 0x20 converts the MSB nibble
        // from 0x50 to 0x70. This ensures that the SOP
        // EOP and ESC codes *never* appear in the data
        // stream as data, they only appear as SOP, EOP,
        // or ESC. This means that async_read_until does
        // not have to check for one, or two character
        // protocol codes.
        //
        static const char SOP;
        static const char EOP;
        static const char ESC;
        static const char MASK;

        chat_message(int max_length = 512)
        : buffer_(new boost::asio::streambuf(max_length))
        {
        }

        // Stream buffer access
        const boost::asio::streambuf& buffer() const { return *buffer_;}

        // Stream buffer access
        boost::asio::streambuf& buffer() {return *buffer_;}

        // Encode to streambuf
        bool encode(const std::string &message);

        // Decode from streambuf
        bool decode(std::string &message);

private:
        std::shared_ptr<boost::asio::streambuf> buffer_;
};

// Protocol codes
const char chat_message::SOP = 0x5B;
const char chat_message::EOP = 0x5D;
const char chat_message::ESC = 0x5C;
const char chat_message::MASK = 0x20;

// Encode to streambuf
bool
chat_message::encode(const std::string &message)
{
        size_t size = message.size();
        std::ostream os(buffer_.get());

        // Start-of-packet
        os << '[';

        // Encode message
        for (size_t i = 0; i < size; i++) {
                char c = message[i];
                switch (c) {
                        case SOP:
                        case EOP:
                        case ESC:
                                os << (char)ESC;
                                os << (char)(c ^ MASK);
                                break;

                        default:
                                os << message[i];
                                break;
                }
        }

        // End-of-packet
        os << ']';

        return os.good();
}

// Decode from streambuf
// * the code uses is.get(char &c) rather than is >> c, so
// that all ASCII values are decoded
bool
chat_message::decode(std::string &message)
{
        size_t size = buffer_->size();
        std::istream is(buffer_.get());

        // Start-of-packet
        char c;
        is.get(c);
        assert (c == '[');
        size--;

        // Decode message buffer
        bool done = false;
        while(!done && size-- && is.get(c)) {
                switch (c) {

                        case SOP:
                                // Should not occur
                                assert (false);
                                break;

                        case ESC:
                                is >> c;
                                message.push_back(c ^ MASK);
                                break;

                        case EOP:
                                done = true;
                                break;

                        default:
                                message.push_back(c);
                                break;
                }
        }
        return done;
}

#endif // CHAT_MESSAGE_HPP


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net