Wiederverwendung von boost::asio::ip::tcp::acceptor
-
Huhu!
Folgender Code-Abschnitt:
void server::run(tcp::endpoint endpoint, int backlog){ if(acceptor.is_open()){ acceptor.close(); connections.clear(); acceptor.get_io_service().run(); } acceptor.open(endpoint.protocol()); acceptor.set_option(tcp::socket::reuse_address(true)); acceptor.bind(endpoint); acceptor.listen(backlog); accept(); } void server::accept(){ std::shared_ptr<tcp::socket> socket = std::make_shared<tcp::socket>( acceptor.get_io_service() ); acceptor.async_accept( *socket, boost::bind(&server::handle_accept, this, socket, boost::asio::placeholders::error) ); }
Die run()-Methode soll sozusagen den Server (neu)starten und anschließend auf Klienten warten. Wenn die run()-Methode dann ein weiteres Mal aufgerufen wird, sollen alle (in einem Container gehaltenen) Verbindungen mitsamt dem Acceptor geschlossen (siehe Zeilen 3 und 4) und den Acceptor dann mit frischen Werten gefüttert werden (siehe Zeilen 8 - 12).
Wenn ich die run()-Methode jedoch ein zweites Mal aufrufe, wirft die async_accept()-Methode eine "Bad file descriptor"-Exception und ich versteh' grad nicht warum. Versucht habe ich bisher einen acceptor::cancel()-Aufruf vor dem schließen des Acceptors, sowie folgendes:if(acceptor.is_open()){ connections.clear(); boost::asio::io_service& io_service = acceptor.get_io_service(); acceptor.~basic_socket_acceptor(); new(&acceptor) tcp::acceptor{io_service}; io_service.run(); }
Beides wirft die gleiche "Bad file descriptor"-Exception.
Jemand eine Ahnung?
-
Evtl. bin ich ein alter Sack und gegenüber Provokationen nicht mehr so resistent wie früher...
Aber bei Deinem Nick habe ich noch nicht mal Lust Deinen Post zu lesen.Schönen Gruß,
FW
-
Warum innerhalb von
server
denacceptor
wiederverwenden (noch dazu mit placement new)? Wirf einfach die Instanz vonserver
weg (=im dtor die Verbindungen schliessen, etc.) und erzeuge eine neue Instanz.
-
Furble Wurble schrieb:
Evtl. bin ich ein alter Sack und gegenüber Provokationen nicht mehr so resistent wie früher...
Aber bei Deinem Nick habe ich noch nicht mal Lust Deinen Post zu lesen.Schönen Gruß,
FWNicht doch.
Damit wollte ich niemanden provozieren.Sorry wenns so ankam.
-
theta schrieb:
Warum innerhalb von
server
denacceptor
wiederverwenden (noch dazu mit placement new)? Wirf einfach die Instanz vonserver
weg [...] und erzeuge eine neue Instanz.Das habe ich soeben versucht. Wirft die gleiche Exception. Hab die Server-Instanz einem std::shared_ptr<> überlassen. Jedesmal, wenn der Server neu gestartet werden soll, wird srv.reset(new server{io_service}); vor der run()-Methode aufgerufen.
theta schrieb:
(=im dtor die Verbindungen schliessen, etc.)
Das sollte doch eigentlich automatisch über die Bühne laufen. Der Destruktor des Acceptors wird aufgerufen und der Destruktor des std::vector<connection> ruft alle Destruktoren seiner Elemente auf, unter anderem auch die von tcp::socket. Auch wenn ich den Destruktor selber schreibe (eben probiert), wird die gleiche Exception geworfen. Hier noch ein Stückchen Code, falls der noch was aussagen könnte:
void start_server(...){ ... srv.reset(new server{io_service}); // io_service.run(); <=== Dies erzeugt eine "Operation canceled"-Exception srv.run(tcp::endpoint{tcp::v4(), port}, backlog); }
-
Ich schlage vor, dass du ein vollständiges Bsp. postest, welches den Fehler zeigt. Das hilft sicher am meisten um dein konkretes Problem zu lösen.
Hier trotzdem einige Gedanken...
In der
start_server(..)
Funktion ist die Reihenfolge falsch zu sein:Die korrekte Reihenfolge muss sein:
acceptor
erzeugen (inkl. öffnen, listen etc.) undasync_accept(..)
aufrufen -> es werden noch keine Verbindungsanfragen prozessiertio_service::run()
aufrufen -> erst jetzt prozessiert der Server Verbindungsanfragen
io_service::run
kehrt zurück, sobald keine Aufträge (=pendingasync_accept(..)
mehr offen sind, d.h. du musst es rekursiv aufrufen fall mehr als eine Verbindung angenommen werden soll)
Auf deine
start_server(..)
Funktion angewendet:void start_server(...){ // ... boost::asio::io_service io_service; srv.reset(new server{io_service}); srv->run(tcp::endpoint{tcp::v4(), port}, backlog); io_service.run(); }
Der einzige Grund warum es
server::run(..)
brauchen könnte, ist wenn du mitenable_shared_from_this
arbeiten tätest - falls nicht: ab in den Konstruktor damit.
-
Hmhm, okay.
Ich hab mein Projekt etwas umdesigned, sodass es nun funktioniert.
Die genaue Ursache, die die Exception geworfen hat, kenne ich leider nicht. Jedenfalls klappt es nun, nachdemboost::asio::io_service
Member vonserver
ist und dieserver
-Instanz bei jedem Neustart wieder resettet wird.Das Problem ist somit gelöst.
Danke für die Hilfe!