2015-02-14 2 views
2

Моего кода выглядит следующим образом:вопросы об использовании async_write сразу после async_read_until

boost::asio::streambuf b1; 

boost::asio::async_read_until(upstream_socket_, b1, '@', 
      boost::bind(&bridge::handle_upstream_read, shared_from_this(), 
      boost::asio::placeholders::error, 
      boost::asio::placeholders::bytes_transferred)); 


void handle_upstream1_read(const boost::system::error_code& error, 
          const size_t& bytes_transferred) 
    { 
    if (!error) 
    { 

     async_write(downstream_socket_, 
      b2, 
      boost::bind(&bridge::handle_downstream_write, 
      shared_from_this(), 
      boost::asio::placeholders::error)); 
    } 
    else 
     close(); 
    } 

Согласно документации async_read_until, http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference/async_read_until/overload1.html, После успешной операции async_read_until, то streambuf может содержать дополнительные данные за пределами разделителя. Приложение обычно оставляет эти данные в streambuf для последующей операции async_read_until для проверки.

Я знаю, что streambuf может содержать дополнительные данные за пределами разделителя, но в моем случае он будет записывать эти дополнительные данные (данные за пределами char '@') в downstream_socket_ внутри операции async_write? Или функция async_write достаточно умна, чтобы не писать эти дополнительные данные до следующего вызова функции handle_upstream1_read?

В соответствии с подходами в документации, данные в streambuf сохраняются в IStream первой (станд :: IStream response_stream (& streambuf);) , а затем поместить его в строку, используя зЬй :: GetLine() Funciton.

Нужно ли сначала хранить streambuf в istream, а затем преобразовать его в строку и затем преобразовать обратно в char arrary (чтобы я мог отправить массив char в downstream_socket_) вместо того, чтобы просто использовать async_write для записать данные (до, но не включая разделитель, '@') в downstream_socket_?

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

Мой идеальный случай, что:

  1. upstream_socket_ получил хххх @ гггг с помощью async_read_until
  2. хххх @ записывается в downstream_socket_
  3. upstream_socket_ получил ZZZZ @ KKKK с помощью async_read_until
  4. yyyyzzzz @ записывается в downstream_socket_

Кажется, что async_write ope ration все еще записывает данные за пределы разделителя в downstream_socket_. (но я не уверен в этом на 100%)

Я ценю, если кто-нибудь может немного помочь!

ответ

3

Используемая перегрузка async_write() считается заполненной, когда все данные streambuf, ее входная последовательность, были записаны в WriteStream (сокет). Это эквивалентно вызову:

boost::asio::async_write(stream, streambuf, 
    boost::asio::transfer_all(), handler); 

Можно ограничить количество байт, записанных и потребленных от объекта streambuf, вызвав эту async_write() перегрузки с условием boost::asio::transfer_exactly завершения:

boost::asio::async_write(stream, streambuf, 
    boost::asio::transfer_exactly(n), handler); 

В качестве альтернативы, можно записать непосредственно из входной последовательности streambuf. Тем не менее, нужно будет явно потреблять из streambuf.

boost::asio::async_write(stream, 
    boost::asio::buffer(streambuf.data(), n), handler); 
// Within the completion handler... 
streambuf.consume(n); 

Обратите внимание, что, когда async_read_until() операции завершается, bytes_transferred аргумент обработчика завершающего содержит количество байт в последовательности ввода в streambuf в вплоть до и включая разделитель, или 0, если произошла ошибка.

Полный пример demonstrating с использованием обоих подходов. Пример написан с использованием синхронных операций в попытке упростить поток:

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

// This example is not interested in the handlers, so provide a noop function 
// that will be passed to bind to meet the handler concept requirements. 
void noop() {} 

/// @brief Helper function that extracts a string from a streambuf. 
std::string make_string(
    boost::asio::streambuf& streambuf, 
    std::size_t n) 
{ 
    return std::string(
     boost::asio::buffers_begin(streambuf.data()), 
     boost::asio::buffers_begin(streambuf.data()) + n); 
} 

int main() 
{ 
    using boost::asio::ip::tcp; 
    boost::asio::io_service io_service; 

    // Create all I/O objects. 
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0)); 
    tcp::socket server_socket(io_service); 
    tcp::socket client_socket(io_service); 

    // Connect client and server sockets. 
    acceptor.async_accept(server_socket, boost::bind(&noop)); 
    client_socket.async_connect(acceptor.local_endpoint(), boost::bind(&noop)); 
    io_service.run(); 

    // Mockup write_buffer as if it read "[email protected]" with read_until() 
    // using '@' as a delimiter. 
    boost::asio::streambuf write_buffer; 
    std::ostream output(&write_buffer); 
    output << "[email protected]"; 
    assert(write_buffer.size() == 9); 
    auto bytes_transferred = 5; 

    // Write to server. 
    boost::asio::write(server_socket, write_buffer, 
     boost::asio::transfer_exactly(bytes_transferred)); 
    // Verify write operation consumed part of the input sequence. 
    assert(write_buffer.size() == 4); 

    // Read from client. 
    boost::asio::streambuf read_buffer; 
    bytes_transferred = boost::asio::read(
     client_socket, read_buffer.prepare(bytes_transferred)); 
    read_buffer.commit(bytes_transferred); 

    // Copy from the read buffers input sequence. 
    std::cout << "Read: " << 
       make_string(read_buffer, bytes_transferred) << std::endl; 
    read_buffer.consume(bytes_transferred); 

    // Mockup write_buffer as if it read "[email protected]" with read_until() 
    // using '@' as a delimiter. 
    output << "[email protected]"; 
    assert(write_buffer.size() == 13); 
    bytes_transferred = 9; // [email protected] 

    // Write to server. 
    boost::asio::write(server_socket, buffer(write_buffer.data(), 
     bytes_transferred)); 
    // Verify write operation did not consume the input sequence. 
    assert(write_buffer.size() == 13); 
    write_buffer.consume(bytes_transferred); 

    // Read from client. 
    bytes_transferred = boost::asio::read(
     client_socket, read_buffer.prepare(bytes_transferred)); 
    read_buffer.commit(bytes_transferred); 

    // Copy from the read buffers input sequence. 
    std::cout << "Read: " << 
       make_string(read_buffer, bytes_transferred) << std::endl; 
    read_buffer.consume(bytes_transferred); 
} 

Выходные:

Read: [email protected] 
Read: [email protected] 

Несколько других примечаний:

  • streambuf владеет память , и std::istream и std::ostream используйте память. Использование потоков может быть хорошей идеей, когда нужно извлечь форматированный вход или вставить форматированный вывод. Например, когда вы хотите прочитать строку "123" как целое число 123.
  • Доступ к входной последовательности streambuf можно осуществлять напрямую и перебирать по ней. В приведенном выше примере я использую , чтобы помочь построить std::string путем итерации по входной последовательности streambuf.

    std::string(
        boost::asio::buffers_begin(streambuf.data()), 
        boost::asio::buffers_begin(streambuf.data()) + n); 
    
  • Протокол потока на основе транспортного используется, поэтому обрабатывать входящие данные в виде потока. Имейте в виду, что даже если промежуточный сервер перефразирует сообщения и отправляет "[email protected]" в одну операцию записи и "[email protected]" в последующей операции записи, нисходящий поток может читать "[email protected]" в одной операции чтения.

+0

Благодарим вас за подробное и подробное объяснение, Таннер. transfer_exactly (n) и streambuf.consume (n) - именно то, что я хочу! Причина, по которой я так долго задерживался, заключается в том, что я попытался использовать единственный символ «@», чтобы действовать как символ конечного тега в моих данных видеопотока, который полностью является дамп-идеей. – hclee

+0

В этом примере используется write(), который я Предположим, что синхронный boost :: asio :: write(), правильно? – Dronz

+2

@Dronz Да. [Аргумент-зависимый поиск] (http://en.wikipedia.org/wiki/Argument-dependent_name_lookup) заставил компилятор выбрать 'boost :: asio :: read()', 'boost :: asio :: write() ',' boost :: asio :: buffers_begin() ', потому что один из их аргументов также находился в пространстве имен' boost :: asio'. Независимо от того, я изменил пример, чтобы быть более явным. –

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