2016-07-18 10 views
1

Mein Programm fungiert als Server, zu dem ein Client eine Verbindung herstellen kann. Sobald ein Client verbunden ist, erhält er alle 5 Sekunden Updates vom Server. Dies ist die write -function, die alle 5 Sekunden zu senden und die neuen Daten an den Client aufgerufen wird:Boost.Asio: Zeitüberschreitung für asynchrone Vorgänge

void NIUserSession::write(std::string &message_orig) 
{ 
    std::cout << "Writing message" << std::endl; 

    std::shared_ptr<std::string> message = std::make_shared<std::string>(message_orig); 
    message->append("<EOF>"); 
    boost::system::error_code ec; 
    boost::asio::async_write(this->socket_, boost::asio::buffer(*message), 
     boost::asio::transfer_all(), boost::bind(&NIUserSession::writeHandler, 
       this, boost::asio::placeholders::error, 
       boost::asio::placeholders::bytes_transferred(), 
       message 
       )); 
} 

void NIUserSession::writeHandler(const boost::system::error_code &error, std::size_t bytes_transferred, std::shared_ptr<std::string> message) 
{ 
    std::cout << "Write Handler" << std::endl; 
    if(error) 
    { 
     std::cout << "Write handler error: " << error.message() << std::endl; 
     this->disconnect(); 
    } 
} 

void NIUserSession::disconnect() 
{ 
    std::cout << "Disconnecting client, cancling all write and read operations." << std::endl; 
    this->socket_.lowest_layer().cancel(); 

    delete this; 
} 

Wenn ein Fehler in den Schreiboperationen ist die Verbindung zwischen dem Server und dem Client geschlossen wird und alle Asynchron Operationen sind cancled (this->socket_.lowest_layer().cancel();).

Das Problem ist, dass wenn die Verbindung abläuft, writeHandler nicht sofort aufgerufen wird. Stattdessen "stapeln" die Schreiboperationen, bis die erste writeHandler erreicht.

Dies sollte die normale Ausgabe des Programms sein:

Writing message 
Write Handler 
... Other stuff ... 
... Other stuff ... 
Writing message 
Write Handler 

Wenn die Verbindungen mal aus, das ist, was passiert:

Writing message 
Write Handler 
Write handler error: Connection timed out 
Disconnecting client, cancling all write and read operations. 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Segmentation fault 

Am Ende steigt ein Segmentierungsfehler. Ich denke, das ist, weil disconnect aufgerufen wird, während andere asynchrone Operationen noch auf ihrem Weg sind. Ich dachte, ich könnte es vermeiden, indem Sie this->socket_.lowest_layer().cancel(); direkt nach der ersten asynchronen Operation fehlschlägt, aber es funktioniert nicht.

Wie kann ich einen Segmentierungsfehler vermeiden?

Antwort

3

Nun, Sie sollten this nicht löschen, wenn Sie die Operationen abbrechen, da die Callbacks für die ausstehenden E/A-Operationen immer noch aufgerufen werden und der Zugriff auf this zu undefiniertem Verhalten führt. Es gibt mehrere Möglichkeiten, dies zu beheben:

  1. Schreiben Sie keine Daten, bis Sie tatsächlich wissen, dass vorherige Daten geschrieben wurden. Sie können die std::string-Instanzen, die an NIUserSession::write übergeben werden, in den Wartestatus einreihen, wenn ein ausstehender Schreibvorgang noch aussteht und sie dann tatsächlich in den Handler schreiben, wenn der ausstehende Schreibvorgang abgeschlossen ist. Auf diese Weise haben Sie nicht mehrere E/A-Operationen im Flug.
  2. vererben std::enable_shared_from_this und shared_from_this() statt this zum async_write Aufruf übergeben (das ist, was die Boost asynchronous TCP daytime server example tut). Auf diese Weise behalten ausstehende E/A-Vorgänge einen Verweis auf Ihre Klasse und der Destruktor wird aufgerufen, wenn alle abgeschlossen sind.
+0

Es führt nicht zu einem Segmentierungsfehler. Es führt zu [Undefined Behaviour] (https://en.wikipedia.org/wiki/Undefined_behavior). Sonst, +1 – sehe

+0

Sie müssen bei der Verwendung von asynchronen Operationen sehr vorsichtig mit Objektlebensdauern umgehen. Die 'boost :: asio' [Beispiele] (http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/examples/cpp11_examples.html) verwenden' shared_ptr' aus gutem Grund, siehe: [ boot asio async-funktion und shared ptrs] (http://stackoverflow.com/questions/11356742/boost-async-functions-and-shared-ptrs/19622084#19622084) – kenba

+0

@sehe - danke, bearbeitet :) Mein schlechtes für die Verwendung die falsche Formulierung. –

Verwandte Themen