2016-05-22 2 views
1

Я использую TCP-сервер, который использует asio socket.async_read() и boost :: asio :: async_read_until() методы для асинхронных данных чтения из сокета. Оба используют один и тот же обработчик для чтения данных из boost :: asio :: streambuf.boost :: asio :: streambuf - как повторно использовать буфер?

Обработчик, который прекрасно работает вызывается через async_read():

void handle_read(const boost::system::error_code& ec, std::size_t ytes_transferred)) 
{ 
    m_request_buffer.commit(bytes_transferred); 
    boost::asio::streambuf::const_buffers_type rq_buf_data = m_request_buffer.data(); 
    std::vector<uint8_t> dataBytes(boost::asio::buffers_begin(rq_buf_data), boost::asio::buffers_begin(rq_buf_data) + bytes_transferred); 

    //process data here 

    m_request_buffer.consume(bytes_transferred); 
    bytes_transferred = 0; 
} 

Мой сервер в зависимости от обработки данных может подключение выключения или продолжить чтение через тот же сокет.

Но, если handle_read() вызывается из вызова 2-го boost :: asi :: async_read_until(), я получаю количество нулей в dataBytes, а затем верны действительные данные.

Я попробовал простой тестовый чехол и узнал, что после записи данных в streambuf и commit() + потребляет() данные в streambuf по-прежнему сохраняют предыдущий буфер.

Итак, есть ли способ очистить данные в boost :: asio :: streambuf и повторно использовать его в boost :: asio :: async_read_until()?

Live Coliru

Если скомпилирован с USE_STREAM = 1, живой пример работает отлично. Но что std :: istream делает по-разному по сравнению с buffer consumume()?

ответ

8

При использовании операций Boost.Asio, которые работают на streambuf или потоковых объектов, которые используют streambuf, такие как std::ostream и std::istream, лежащие в основе последовательности входных и выходных будут надлежащим образом. Если вместо этого предоставляется буфер для операции, например, прохождение prepare() к операции чтения или data() в операцию записи, тогда необходимо явно обработать commit() и consume().

Проблема в примере заключается в том, что она нарушает контракт API, в результате чего неинициализированная память должна быть привязана к входной последовательности. Документация commit() гласит:

Требуется предыдущий вызов prepare(x) где x >= n, и никаких промежуточных операций, которые изменяют последовательность ввода или вывода.

Использование std::ostream между prepare() и commit() нарушает этот договор, так как он будет изменять последовательность входа:

// Prepare 1024 bytes for the output sequence. The input sequence is 
// empty. 
boost::asio::streambuf streambuf; 
streambuf.prepare(1024); 

// prepare() and write to the output sequence, then commit the written 
// data to the input sequence. The API contract has been violated. 
std::ostream ostream(&streambuf); 
ostream << "1234567890"; 

// Commit 10 unspecified bytes to the input sequence. Undefined 
// behavior is invoked. 
streambuf.commit(10); 

Вот полный пример demonstrating использования streambuf с аннотациями комментариев:

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

int main() 
{ 
    std::cout << "with streams:" << std::endl; 
    { 
    boost::asio::streambuf streambuf; 

    // prepare() and write to the output sequence, then commit the written 
    // data to the input sequence. The output sequence is empty and 
    // input sequence contains "1234567890". 
    std::ostream ostream(&streambuf); 
    ostream << "1234567890"; 

    // Read from the input sequence and consume the read data. The string 
    // 'str' contains "1234567890". The input sequence is empty, the output 
    // sequence remains unchanged. 
    std::istream istream(&streambuf); 
    std::string str; 
    istream >> str; 
    std::cout << "str = " << str << std::endl; 

    // Clear EOF bit. 
    istream.clear(); 

    // prepare() and write to the output sequence, then commit the written 
    // data to the input sequence. The output sequence is empty and 
    // input sequence contains "0987654321". 
    ostream << "0987654321"; 

    // Read from the input sequence and consume the read data. The string 
    // 'str' contains "0987654321". The input sequence is empty, the output 
    // sequence remains unchanged. 
    istream >> str; 
    std::cout << "str = " << str << std::endl; 
    } 

    std::cout << "with streams and manual operations:" << std::endl; 
    { 
    boost::asio::streambuf streambuf; 

    // prepare() and write to the output sequence, then commit the written 
    // data to the input sequence. The output sequence is empty and 
    // input sequence contains "1234567890". 
    std::ostream ostream(&streambuf); 
    ostream << "1234567890"; 

    // Copy 10 bytes from the input sequence. The string `str` contains 
    // "1234567890". The output sequence is empty and the input 
    // sequence contains "1234567890". 
    auto data = streambuf.data(); 
    std::string str(boost::asio::buffers_begin(data), 
        boost::asio::buffers_begin(data) + 10); 
    std::cout << "str = " << str << std::endl; 

    // Consume 10 bytes from the input sequence. The input sequence is 
    // now empty. 
    streambuf.consume(10); 

    // prepare() and write to the output sequence, then commit the written 
    // data to the input sequence. The output sequence is empty and 
    // input sequence contains "0987654321". 
    ostream << "0987654321"; 

    // Copy 10 bytes from the input sequence. The string `str` contains 
    // "0987654321. The output sequence is empty and the input 
    // sequence contains "0987654321".  
    data = streambuf.data(); 
    str.assign(boost::asio::buffers_begin(data), 
       boost::asio::buffers_begin(data) + 10); 
    std::cout << "str = " << str << std::endl; 

    // Consume 10 bytes from the input sequence. The input sequence is 
    // now empty. 
    streambuf.consume(10); 
    } 
} 

Выход:

with streams: 
str = 1234567890 
str = 0987654321 
with streams and manual operations: 
str = 1234567890 
str = 0987654321 

Для получения дополнительной информации о streambuf использования, рассмотрит чтение this ответа.

+1

Спасибо за замечание. Наконец, я посмотрел на boost :: asio :: async_read_until() и выяснил, что это streambuf_.commit (bytes_transferred) ;. Поэтому я не должен называть commit() в моем обработчике.Но в случае использования boost :: asio :: ip :: tcp :: socket.async_read() мой обработчик должен вызвать commit() для перемещения символов из выходной последовательности в последовательность ввода. – drus

+0

@drus нет, вы не должны. Не вмешивайтесь в какие-либо из этих операций. Все, что я когда-либо делал, это вызов 'buffer.consume (buffer.size() + 1)', потому что гарантированно полностью очистить буфер. –

+0

Да, я переключился на использование boost :: asio :: async_read() и boost :: asio :: async_read_until(), и он работает как ожидалось. Как я уже сказал, оба эти метода называют streambuf.commit(). – drus

Смежные вопросы