boost::asio close socket exception
-
Hi,
ich beschäftige mich erst seit wenigen Wochen mit boost::asio, bin von den Konzept sehr angetan, aber verstehe leider noch nicht alle Mechanismen ganz genau.
Kurzer Überlick:
Ich habe einen Server mit boost::asio geschrieben der mehrere Threads aus einem Pool bekommt um die verschiedenen Async-Funktionen abzuarbeiten. Jede neue Verbindung wird durch ein neues Connection Objekt dargestellt. Alle Klassen dieses Servers nutzen boost::shared_ptr.Problemerläuterung:
Wenn die Verbindung nicht durch den Server bzw. einer Eingabe des Benutzers sondern durch den Remote-Host unterbrochen wird, ist es mir nicht möglich die Verbindung ordnungsgemäß zu schließen. Ich fange alle möglichen Exceptions ab und bekomme in diesem Fall die Nachricht: "remote_endpoint: Das angegebene Dateihandle ist ungültig". Mit einem ähnlichen Fehler habe ich bereits zu kämpfen gehabt, ich weis das vermutlich der Socket noch während der Thread läuft beendet wurde. Leider schaffe ich es in dieser Situation (seit geschlagenen 6 Stunden) nicht dieses Problem zu lösen.Ich reduzier den Code erstmal auf ein Minimum, falls weitere Informationen benötigt werden, reiche ich diese nach.
Die einzige aktive Async-Funktion die innerhalb meiner Connection-Klasse zu diesem kritischen Zeitpunkt aktiv ist wird von mir wie folgt aufgerufen:
mSocket->async_read_some(boost::asio::buffer(mSocketReceiveBuffer), bind(&koConnection::onReceive, shared_from_this(), _1, _2));
Hier ist der Handler für die Funktion:
void ko::networking::koConnection::onReceive(const boost::system::error_code & ec, std::size_t bytesReceived) { if(ec) { (*mLog)() << ko::std << "[" << getIPAddress() << "]" << "koConnection::onReceive: error '" << ec.message() << "'" << ko::endl; errorClose(); } else { [...] } }
So schließe ich die Verbindung:
boost::system::error_code ec; mSocket->cancel(); mSocket->shutdown( boost::asio::ip::tcp::socket::shutdown_both, ec ); mSocket->close();
Ich habe bereits verschiedene Möglichkeiten ausprobiert den Socket zu schließen. Es macht allerdings kein Unterschied ob ich ihn direkt aus dem letzten Thread (über errorClose()) aufrufe oder über eine neu erstellte IOService Aufgabe (IOService->post([...])). Der letzte Thread verschwidet nach der OnReceive([...]) Methode mit der oben genannten Exception aus dem Worker-Pool.
Durch die errorClose()-Methode verliert das Connection-Objekt seinen letzten externen shared_ptr. Danach dürften nur noch die Handler shared_ptr auf die Connection haben. Reicht es vielleicht nicht über "shared_from_this()" als Referenzzeiger den Handlern einen shared_ptr zu übergeben - muss der shared_ptr als zusätzlicher Parameter mit übergeben werden?
Ein workaround ist derzeit, auf die Zeile mSocket.close() zu verzichten. So gibts keine Exception und der Server läuft weiter - allerdings kanns das ja auch nicht sein...
Bin für jede Hilfe dankbar!
-
Ich habe noch ein wenig gegooglet und die meisten Beispiele zu Client/Server-Realisierungen mit boost::asio sind Single-Threaded. Da ohne der close() Aufruf die Connection-Objekte fehlerfrei gelöscht werden (und ich nehme mal an der Socket wohl auch), ist dies vielleicht eine gute Lösung für Multithreading. In einigen Kommentaren habe ich gelesen, dass man close() nicht unbedingt aufrufen muss, wenn es keinen besonderen Grund gibt und die Sockets durch ihre Destruktor korrekt geschlossen werden. Die beiden Methoden cancel() und das wichtige shutdown() (für einen sauberen Verbindungsabbruch) lassen sich problemlos aufrufen!
Weis jemand genaueres dazu oder hat ähnliches mal realisiert?