Sia utilizzando un std::istream
e la lettura da esso, come mediante std::getline()
, o invocando esplicitamente boost::asio::streambuf::consume(n)
, rimuove i dati dalla sequenza di ingresso.
Se l'applicazione sta eseguendo una di queste operazioni e successive operazioni read_until()
, i risultati sono duplicati nella sequenza di input di receive_buffer
, quindi è probabile che i dati duplicati provengano dal peer remoto. Se il peer remoto sta scrivendo sul socket e utilizzando direttamente la sequenza di input di streambuf, il peer remoto deve richiamare esplicitamente consume()
dopo ogni operazione di scrittura riuscita.
Come indicato nella documentazione, di successo read_until()
operazioni possono contenere dati aggiuntivi oltre il delimitatore, tra cui delimitatori aggiuntivi. Ad esempio, se si scrive "[email protected]@"
su un socket, l'operazione read_until()
utilizzando '@'
come delimitatore può leggere e memorizzare "[email protected]@"
nella sequenza di input dello streambuf. Tuttavia, l'operazione indicherà che la quantità di byte trasferiti è compresa fino al primo delimitatore. Pertanto, bytes_transferred
sarebbe 2
e streambuf.size()
sarebbe 4
. Dopo che sono stati consumati i byte 2
, la sequenza di input dello streambuf conterrà "[email protected]"
e una chiamata successiva a read_until()
verrà restituita immediatamente, poiché lo streambuf contiene già il delimitatore.
Ecco un esempio completo demonstratingstreambuf
utilizzo per la lettura e la scrittura, e come la sequenza di ingresso è consumato:
#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() {}
std::string make_string(boost::asio::streambuf& streambuf)
{
return {buffers_begin(streambuf.data()),
buffers_end(streambuf.data())};
}
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();
// Write to server.
boost::asio::streambuf write_buffer;
std::ostream output(&write_buffer);
output << "[email protected]"
"[email protected]";
write(server_socket, write_buffer.data());
std::cout << "Wrote: " << make_string(write_buffer) << std::endl;
assert(write_buffer.size() == 4); // Data not consumed.
// Read from the client.
boost::asio::streambuf read_buffer;
// Demonstrate consuming via istream.
{
std::cout << "Read" << std::endl;
auto bytes_transferred = read_until(client_socket, read_buffer, '@');
// Verify that the entire write_buffer (data pass the first delimiter) was
// read into read_buffer.
auto initial_size = read_buffer.size();
assert(initial_size == write_buffer.size());
// Read from the streambuf.
std::cout << "Read buffer contains: " << make_string(read_buffer)
<< std::endl;
std::istream input(&read_buffer);
std::string line;
getline(input, line, '@'); // Consumes from the streambuf.
assert("a" == line); // Note getline discards delimiter.
std::cout << "Read consumed: " << line << "@" << std::endl;
assert(read_buffer.size() == initial_size - bytes_transferred);
}
// Write an additional message to the server, but only consume '[email protected]'
// from write buffer. The buffer will contain '[email protected]@'.
write_buffer.consume(2);
std::cout << "Consumed write buffer, it now contains: " <<
make_string(write_buffer) << std::endl;
assert(write_buffer.size() == 2);
output << "[email protected]";
assert(write_buffer.size() == 4);
write(server_socket, write_buffer.data());
std::cout << "Wrote: " << make_string(write_buffer) << std::endl;
// Demonstrate explicitly consuming via the streambuf.
{
std::cout << "Read" << std::endl;
auto initial_size = read_buffer.size();
auto bytes_transferred = read_until(client_socket, read_buffer, '@');
// Verify that the read operation did not attempt to read data from
// the socket, as the streambuf already contained the delimiter.
assert(initial_size == read_buffer.size());
// Read from the streambuf.
std::cout << "Read buffer contains: " << make_string(read_buffer)
<< std::endl;
std::string line(
boost::asio::buffers_begin(read_buffer.data()),
boost::asio::buffers_begin(read_buffer.data()) + bytes_transferred);
assert("[email protected]" == line);
assert(read_buffer.size() == initial_size); // Nothing consumed.
read_buffer.consume(bytes_transferred); // Explicitly consume.
std::cout << "Read consumed: " << line << std::endl;
assert(read_buffer.size() == 0);
}
// Read again.
{
std::cout << "Read" << std::endl;
read_until(client_socket, read_buffer, '@');
// Read from the streambuf.
std::cout << "Read buffer contains: " << make_string(read_buffer)
<< std::endl;
std::istream input(&read_buffer);
std::string line;
getline(input, line, '@'); // Consumes from the streambuf.
assert("b" == line); // Note "b" is expected and not "c".
std::cout << "Read consumed: " << line << "@" << std::endl;
std::cout << "Read buffer contains: " << make_string(read_buffer)
<< std::endl;
}
}
uscita:
Wrote: [email protected]@
Read
Read buffer contains: [email protected]@
Read consumed: [email protected]
Consumed write buffer, it now contains: [email protected]
Wrote: [email protected]@
Read
Read buffer contains: [email protected]
Read consumed: [email protected]
Read
Read buffer contains: [email protected]@
Read consumed: [email protected]
Read buffer contains: [email protected]
Grazie, questo è un ottimo esempio di lavorando con streambuf. Ho trovato il motivo per cui vedo i dati dei messaggi precedenti dopo l'invio. Sul client, la funzione Send() utilizza un buffer globale streambuf (send_buffer nel mio esempio). Come faccio a fare in modo che ogni chiamata di Send() utilizzi il proprio oggetto streambuf? Qualcosa come boost :: make_shared? Questo è l'approccio giusto? Hai un buon esempio di server e client usando boost :: asio? Questo è quello che nella documentazione di Boost d non mi va bene, ho usato buffer di lunghezza costante, e mi piacerebbe vedere un esempio di utilizzo di streambuf. – vint
Risposta fantastica, molto utile. Vorrei che la gente di Boost ti facesse scrivere tutta la loro documentazione! ;-) – Dronz
Come hai avuto accesso alla sequenza di input senza mai eseguire commit ('commit()')? [data() documentation] (http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/basic_streambuf/data.html) dice 'data()' restituisce l'elenco dei buffer di input, e in base alla [commit() documentation] (http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/basic_streambuf/commit.html), copia effettivamente i caratteri dalla sequenza di output per l'input sequenza. –