Как читать из Boost ASIO streambuf? - PullRequest
       53

Как читать из Boost ASIO streambuf?

4 голосов
/ 05 февраля 2012

Есть ли способ чтения из streambuf без удаления байтов?

Я читаю поле «Размер сообщения» из буфера, чтобы проверить, было ли получено все сообщение.

Если нет, я отправляю еще одно асинхронное чтение, чтобы получить его, но обработчик не может узнать, как долго должно было быть сообщение - потому что поле размера было удалено.

Любая помощь приветствуется!

1009 * Е.Г. *

boost::asio::streambuf _buffer;

void onReceive(const boost::system::error_code& e, std::size_t bytesTransferred)
{
  if(e) return;

  if(_buffer.size() > 0)
  {
    // Partial message was previously received, but I don't know how long.
  }
  else
  {
    _buffer.commit(bytesTransferred);

    /* Read the size (and remove it from the stream) */
    unsigned short size = 0;
    std::istream in(&_buffer);
    in.read((char*)&size, sizeof(unsigned short);

    /* Got the whole message? */
    if(_buffer.size() > size)
    {
      /* Yes. */
    }
    else
    {
      /* No - read the rest. */
      boost::asio::async_read(/*...*/);
    }
  }
}

Ответы [ 3 ]

2 голосов
/ 05 февраля 2012

Вы можете использовать read_async, чтобы инициировать чтение, используя размер заголовка сообщения, а затем настроить его в обратном вызове 'условие завершения', например так:

typedef boost::system::error_code error_code;

template <typename Stream, typename Message>
void MessageReader<Stream, Message>::startRead()
{
  readBuffer = allocateMsg();
  async_read(stream, 
             boost::asio::buffer(readBuffer.get(), sizeof(*readBuffer)),
             boost::bind(&MessageReader<Stream, Message>::bytesToRead, this,
                         boost::asio::placeholders::error, 
                         boost::asio::placeholders::bytes_transferred),
             boost::bind(&MessageReader<Stream, Message>::readDone, this, 
                         boost::asio::placeholders::error, 
                         boost::asio::placeholders::bytes_transferred));
}

template <typename Stream, typename Message>
size_t MessageReader<Stream, Message>::bytesToRead(const error_code& error, 
                                                   size_t bytes_read)
{
  size_t result;

  if (error)
    result = 0;                                         // error - stop reading

  else if (bytes_read < sizeof(CmnMessageHeader))
    result = sizeof(CmnMessageHeader) - bytes_read;     // read rest of header

  else if (readBuffer->header.byteCount > sizeof(*readBuffer))
    result = 0;                                         // bad byte count

  else
    result = readBuffer->header.byteCount - bytes_read; // read message body

  return result;
}

template <typename Stream, typename Message>
void MessageReader<Stream, Message>::readDone(const error_code& error, 
                                              size_t bytes_read)
{
  if (error)
  {
    if (error.value() == boost::system::errc::no_such_file_or_directory)
    {
      notifyStop();
    }

    else if (error.value() != boost::system::errc::operation_canceled)
    {
      notifyStop();
    }

    // else the operation was cancelled, thus no stop notification is needed and
    // we can merely return
  }

  else if (bytes_read != readBuffer->header.byteCount)
  {
    LOG4CXX_ERROR(logger, "Message byte count mismatch");
    notifyStop();
  }

  else
  {
    handleMsg(readBuffer);
    startRead();
  }
}

EDIT: добавлен typedef для error_code.

1 голос
/ 23 июня 2017

Я сделал это вчера.Поэтому я подумал, что предложу свое решение ...

#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>

#include <boost/asio.hpp>
#include <boost/asio/streambuf.hpp>

void ReadFromStreambuf()
{
    boost::asio::streambuf mybuffer;

    // write some data to the buffer
    std::ostream o2buffer (&mybuffer);
    o2buffer << "hello stackoverflow";

    // get buffer size
    size_t nBufferSize = boost::asio::buffer_size(mybuffer.data());

    // get const buffer
    std::stringstream ssOut;
    boost::asio::streambuf::const_buffers_type constBuffer = mybuffer.data();

    // copy const buffer to stringstream, then output
    std::copy(
        boost::asio::buffers_begin(constBuffer),
        boost::asio::buffers_begin(constBuffer) + nBufferSize,
        std::ostream_iterator<char>(ssOut)
    );

    std::cout << ssOut.str() << "\n";
}


int main(int argc, char const *argv[])
{
    ReadFromStreambuf();
    return 0;
}
0 голосов
/ 05 февраля 2012

Существует два подхода, которые вы можете использовать:

  1. Выполните однократное чтение, чтобы прочитать количество байтов для размера (скажем, 4), выпустите чтение для требуемого размера.

  2. Используйте вызов read some, и буферизируйте байты в вашем коде, скажем, в векторе и анализируйте таким образом.

Я бы выбрал вариант 2, это означает копирование буфера, однако я бы рискнул, что это дешевле, чем многократное чтение некоторых вызовов.

...