2016-05-22 7 views
1

Ich bin TCP-Server implementieren, die beide asio socket.async_read() und boost :: asio :: async_read_until() -Methoden zum asynchronen Lesen von Daten aus dem Socket verwendet. Beide verwenden denselben Handler zum Lesen von Daten aus boost :: asio :: streambuf.boost :: asio :: streambuf - Wie man Puffer wiederverwenden kann?

Der Handler, die perfekt funktioniert über async_read aufgerufen():

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; 
} 

Mein Server auf die Verarbeitung von Daten kann Shutdown-Verbindung oder weiterhin über die gleiche Buchse Lesen abhängig.

Aber wenn Handle_read() von der 2. boost :: asi :: async_read_until() aufgerufen wird, bekomme ich eine Anzahl von Nullen in DataBytes und dann gültige Daten geht.

Ich versuchte einen einfachen Testfall und fand heraus, dass nach dem Schreiben von Daten in streambuf und commit() + consume() die Daten in streambuf noch den vorherigen Puffer behalten.

Also gibt es eine Möglichkeit, Daten in boost :: asio :: streambuf zu löschen und in boost :: asio :: async_read_until() wiederzuverwenden?

Live Coliru

Wenn mit USE_STREAM kompiliert = 1, arbeitet das Live-Beispiel in Ordnung. Aber was std :: isorem unterscheidet sich im Vergleich zum Puffer verbrauchen()?

Antwort

8

Wenn Boost.Asio Operationen verwenden, die auf streambuf oder Stream-Objekten arbeiten, die ein streambuf wie std::ostream und std::istream, die zugrunde liegenden Eingangs- und Ausgangssequenzen richtig verwaltet werden, verwenden. Wenn stattdessen ein Puffer für eine Operation bereitgestellt wird, wie z. B. das Übergeben von prepare() an eine Leseoperation oder data() an eine Schreiboperation, dann muss man explizit mit commit() und consume() umgehen.

Das Problem im Beispiel ist, dass es gegen den API-Vertrag verstößt, was zur Folge hat, dass nicht initialisierter Speicher für die Eingabesequenz festgeschrieben wird. Die commit() Dokumentation wird:

Erfordert einen vorhergehenden Anruf prepare(x) wo x >= n und keine dazwischen Operationen, die die Eingabe oder Ausgabesequenz zu modifizieren.

Die Verwendung des std::ostream zwischen dem prepare() und commit() gegen diesen Vertrag, da sie die Eingangssequenz ändern wird:

// 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); 

Hier ist ein komplettes Beispiel demonstrating mit streambuf mit kommentierten Kommentare:

#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); 
    } 
} 

Ausgabe:

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

Weitere Informationen über streambuf Nutzung betrachten this Antwort zu lesen.

+1

Danke für den Kommentar. Schließlich schaute ich auf boost :: asio :: async_read_until() Implementierung und fand heraus, dass es streambuf_.commit (bytes_transfered) ;. Also sollte ich commit() nicht in meinem Handler aufrufen.Aber im Fall der Verwendung von boost :: asio :: ip :: tcp :: socket.async_read() sollte mein Handler eine commit() aufrufen, um Zeichen von der Ausgabesequenz in die Eingabesequenz zu verschieben. – drus

+0

@drus nein, du solltest nicht. Verwechsle dich nicht mit diesen Operationen. Alles, was ich jemals getan habe, war 'buffer.consume (puffer.size() + 1)' zu nennen, da dies garantiert den Puffer vollständig löscht. –

+0

Ja, ich habe Boost :: asio :: async_read() und boost :: asio :: async_read_until() verwendet und es funktioniert wie erwartet. Wie ich schon sagte, rufen diese beiden Methoden streambuf.commit() auf. – drus