2012-02-04 2 views
3

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

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

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

Любая помощь оценена!

E.g.

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(/*...*/); 
    } 
    } 
} 

ответ

2

Вы можете использовать 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: Добавлено ЬурейиМ для Error_Code.

+0

Блестяще, спасибо. – James

0

Есть два подхода, которые Вы можете: принимаете

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

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

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

+0

Спасибо, я принял ваш совет. – James

1

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

#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; 
} 
Смежные вопросы