boost::asio::streambuf ist eine automatisch skalierbare Pufferklasse. Dieser Puffertyp wird häufig verwendet, wenn eine Leseoperation eingeleitet wird, deren Vervollständigung auf dem Inhalt der Daten beruht und nicht notwendigerweise auf der Größe der Daten. Zum Beispiel kann man boost::asio::read_until()
verwenden, um bis zu einem Zeilenumbruch zu lesen, ohne zu wissen oder anzugeben, wie viele Daten gelesen werden dürfen.
Im Fall eines Anwendungsprotokolls mit einem Header fester Größe, das die Länge des Rumpfs enthält und auf den Header ein Körper variabler Länge folgt, sollten Sie einen Puffertyp wie std::vector<>
verwenden. Dies wird das gleiche Maß an Flexibilität als boost::asio::streambuf
bieten, während ein Teil der Buchhaltung zu vereinfachen:
std::vector<char> buffer;
// Read header.
buffer.resize(protocol::header_size);
boost::asio::read(socket, boost::asio::buffer(buffer));
// Extract body size from header, resize buffer, then read
// body.
auto body_size = parse_header(buffer);
buffer.resize(body_size);
boost::asio::read(socket, boost::asio::buffer(buffer));
process_body(buffer);
nicht, wie die vector
Ändern der Größe gibt an, wie viele Daten in den Leseoperationen gelesen werden. Wenn streambuf
verwenden, muss man die Eingangs- und Ausgangssequenzen direkt mit diesen Operationen verwalten:
boost::asio::streambuf streambuf;
// Read header into the streambuf's output sequence.
auto bytes_transferred = boost::asio::read(socket,
streambuf.prepare(protocol::header_size));
// Commit read data from output sequence into the input
// sequence.
streambuf.commit(bytes_transferred);
// Extract body size from header. This would likely
// consume all of the streambuf's input sequence.
auto body_size = parse_header(streambuf);
// Clear the input sequence.
streambuf.consume(streambuf.size());
// Ready body into the streambuf's output sequence.
bytes_transferred = boost::asio::read(socket,
streambuf.prepare(body_size));
// Commit read data from output sequence into the input
// sequence.
streambuf.commit(bytes_transferred);
// Extract all of stream into the body.
process_body(streambuf);
Hier ist ein komplettes Beispiel demonstrating dieser Ansatz:
#include <array> // std::array
#include <functional> // std::bind
#include <iostream> // std::cout, std::endl
#include <vector> // std::vector
#include <boost/asio.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() {}
// The application protocol will consists of a fixed-size header
// containing a std::size_t with the length of the following
// variable length body. To keep it simple, some details
// are ommitted, such as endian handling.
namespace protocol {
enum
{
header_size = sizeof(std::size_t)
};
} // namespace protocol
std::vector<char> build_header(const std::string& body)
{
std::vector<char> buffer(protocol::header_size);
auto body_size = body.size();
std::memcpy(&buffer[0], &body_size, sizeof body_size);
return buffer;
}
std::size_t parse_header(const std::vector<char>& buffer)
{
return *reinterpret_cast<const std::size_t*>(&buffer[0]);
}
int main()
{
using boost::asio::ip::tcp;
// Create all I/O objects.
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
tcp::socket socket1(io_service);
tcp::socket socket2(io_service);
// Connect the sockets.
acceptor.async_accept(socket1, std::bind(&noop));
socket2.async_connect(acceptor.local_endpoint(), std::bind(&noop));
io_service.run();
io_service.reset();
// Write a message from socket1 to socket2.
std::string test_message = "this is a test message";
{
auto header = build_header(test_message);
// Gather header and body into a single buffer.
std::array<boost::asio::const_buffer, 2> buffers = {{
boost::asio::buffer(header),
boost::asio::buffer(test_message)
}};
// Write header and body to socket.
boost::asio::write(socket1, buffers);
}
// Read from socket2.
{
// Use a vector to allow for re-sizing based on the
// amount of data needing to be read. This also reduces
// on the amount of reallocations if the vector is reused.
std::vector<char> buffer;
// Read header.
buffer.resize(protocol::header_size);
boost::asio::read(socket2, boost::asio::buffer(buffer));
// Extract body size from header, resize buffer, then read
// body.
auto body_size = parse_header(buffer);
buffer.resize(body_size);
boost::asio::read(socket2, boost::asio::buffer(buffer));
// Verify body was read.
assert(std::equal(begin(buffer), end(buffer),
begin(test_message)));
std::cout << "received: \n"
" header: " << body_size << "\n"
" body: ";
std::cout.write(&buffer[0], buffer.size());
std::cout << std::endl;
}
}
Ausgang:
received:
header: 22
body: this is a test message
Wissen Sie, in welcher Zeile es defaults? – Tobias
Sorry nein, aber es hat mit dem Streambuf zu tun. – Shuzheng
Erstens, warum verwenden Sie nicht einen regulären Puffer fester Größe, wenn Sie bereits wissen, wie viele Oktette/Bytes Sie füllen werden? Zweitens denke ich, dass die Verwendung von 'boost :: asio :: async_read_until' oder' boost :: asio :: async_read_some' mehr Sinn macht in Bezug auf dein ursprüngliches Problem. – Tobias