0xee
0xee

Reputation: 63

How to design a custom IO object for Boost.Asio

I have an base class (DeviceBase) representing an embedded device, with which I want to communicate. The device can be accessed in various ways, including USB and TCP sockets. Additionally, there is a mock implementation which works on files.

Until now, I have only used synchronous read/write calls, and all the implementations are simply classes derived from the base class, overriding the read/write functions. This allows me to use polymorphic pointers and containers to provide implementation-independent access to the device to the application's logic.

Now i want to use Boost.Asio to enable simple async IO. I have found this guide http://www.highscore.de/cpp/boost/asio.html describing how to write your own extensions, but it is quite simplified, and I have stumbled upon some issues which aren't discussed there.

Is there any form of documentation, or any books which discuss the design of Boost.Asio extensions? Do I overlook something?

Upvotes: 6

Views: 3810

Answers (1)

kenba
kenba

Reputation: 4549

It sounds like an inheritance based solution may be more appropriate for you.

Here’s an example base class using boost::signals2 to signal a received message:

class Connection
{
public:

  typedef boost::signals2::signal<void (const std::vector<char>&)>
     PacketReceived;

protected:
  PacketReceived packet_received_;
  size_t max_rx_packet_size_;
  std::vector<char> read_buffer_;
  std::deque<std::vector<char> > tx_queue_;

  void read_handler(boost::system::error_code const& error,
                    size_t bytes_transferred)
  {
    if (boost::asio::error::operation_aborted != error)
    {
      if (error)
        ; // TODO handle the error, it may be a disconnect.
      else
      {
        read_buffer_.resize(bytes_transferred);
        packet_received_(read_buffer_);
        enable_reception(max_rx_packet_size_);
      }
    }
  }

  void write_handler(boost::system::error_code const& error,
                     size_t bytes_transferred)
  {
    if (boost::asio::error::operation_aborted != error)
    {
      tx_queue_.pop_front();
      if (error)
        ; // TODO handle the error, it may be a disconnect.
      else
        if (!tx_queue_.empty())
          transmit();
    }
  }

  virtual void receive() = 0;

  virtual void transmit() = 0;

  explicit Connection() :
    packet_received_(),
    max_rx_packet_size_(),
    read_buffer_(),
    tx_queue_()
  {}

public:

  virtual void close() = 0;

  virtual ~Connection()
  {}

  void connectPacketReceived(const PacketReceived::slot_type& slot)
  { packet_received_.connect(slot); }

  void enable_reception(size_t max_rx_packet_size)
  {
    max_rx_packet_size_ = max_rx_packet_size;
    receive();
  }

  const std::vector<char>& read_buffer() const
  { return read_buffer_; }

#if defined(BOOST_ASIO_HAS_MOVE)
  void send(std::vector<char>&& packet )
#else
  void send(const std::vector<char>& packet )
#endif
  {
    bool queue_empty(tx_queue_.empty());
    tx_queue_.push_back(packet);
    if (queue_empty)
      transmit();
  }
};

And here's the outline of a class to implement an SSL socket:

class SslConnection :
    public Connection,
    public boost::enable_shared_from_this<SslConnection>
{
  boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket_;

  virtual void receive()
  {
    ssl_socket_.async_read_some(boost::asio::buffer(read_buffer_),
         boost::bind(&SslConnection::read_handler, shared_from_this(),
                     boost::asio::placeholders::error,
                     boost::asio::placeholders::bytes_transferred));
  }

  virtual void transmit()
  {
    boost::asio::async_write(ssl_socket_,
                             boost::asio::buffer(tx_queue_.front()),
         boost::bind(&SslConnection::write_handler, shared_from_this(),
                     boost::asio::placeholders::error,
                     boost::asio::placeholders::bytes_transferred));
  }

  SslConnection(boost::asio::io_service&   io_service,
                boost::asio::ssl::context& ssl_context) :
    Connection(),
    ssl_socket_(io_service, ssl_context)
  {}

public:

  static boost::shared_ptr<SslConnection> create
                     (boost::asio::io_service&   io_service,
                      boost::asio::ssl::context& ssl_context)
  {
    return boost::shared_ptr<SslConnection>
        (new SslConnection(io_service, ssl_context));
  }

  virtual void close()
  {
    boost::system::error_code ignoredEc;
    ssl_socket_.lowest_layer().close(ignoredEc);
  }
};

Upvotes: 2

Related Questions