2016-03-30 4 views
2

Asio: Gibt es einen automatisch in der Größe veränderbaren Puffer für den Empfang von Eingaben?Asio: Gibt es einen automatisch in der Größe veränderbaren Puffer zum Empfangen von Eingaben?

Ich weiß nicht im Voraus die Größe zu erhalten, also sende ich diese Menge in einer Kopfzeile.

Ich habe http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/example/cpp03/chat/chat_message.hpp für ein Beispiel mit einer Kopfzeile betrachtet, aber dieses Beispiel geht von der Angabe einer maximalen Körpergröße aus.

Mit Blick auf die Asio :: Puffer-Klasse muss ich einige zugrunde liegenden Puffer bereitstellen, also nicht flexibel. Stattdessen habe ich in Richtung der asio :: streambuf-Klasse geschaut, aber die Verwendung wie folgt ergibt Segmentierungs-/Speicherfehler.

Ich versuche, eine maximale Größe zu geben, nur HEADER_LEN Bytes, d. H. Die Kopfzeile.

Ist dieser Ansatz falsch?

void do_recv_header() 
{ 
    asio::streambuf buf(HEADER_LEN); 
    asio::async_read(*g_selected_conn, buf, [this, &buf](const system::error_code& ec, std::size_t bytes_transferred) 
    { 
     if (ec != 0) { 
      std::cout << "async_read() error: " << ec.message() << " (" << ec.value() << ") " << std::endl; 
      remove_closed_conn(g_selected_conn); 
      SetEvent(g_wait_event); 
     } 
     else { 
      std::istream is(&buf); 
      int body_len; 
      is >> body_len; 
      std::cout << body_len << std::endl; 
      do_recv_body(body_len); 
     } 
    }); 
} 
+0

Wissen Sie, in welcher Zeile es defaults? – Tobias

+0

Sorry nein, aber es hat mit dem Streambuf zu tun. – Shuzheng

+0

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

Antwort

4

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 
+0

Ausgezeichnet! Könnte es mit Streambuf getan werden? Ich bin verwirrt wegen der Eingabe- und Ausgabepuffer-Kombination von streambuf ... Könnte die Parse-Header-Funktion mit atoi() umgeschrieben werden? Sie verwenden, dass ein Vektor als Array implementiert ist, oder? Ich war mir nicht bewusst, dass ein Vektor in buffer() so verwendet werden könnte. – Shuzheng

+0

Warum rufst du auf io_service zurück? – Shuzheng

+0

@Nicolas Es kann sicherlich mit einem streambuf und atoi ([demo] (http://coliru.stacked-crooked.com/a/03f9f61943f979cd)) getan werden.Diese [Antwort] (http://stackoverflow.com/a/31992879/1053968) kann helfen, 'streambuf' zu verstehen. –

Verwandte Themen