boost::asio - Asynchroner Aufruf nach beenden des sockets



  • Hallo

    Ich habe die Vermutung einen Bug in boost::asio gefunden zu haben.

    Prinzipiell mache ich folgendes:

    boost::asio::io_service io_service;
    boost::asio::ip::udp::socket socket;
    ...
    socket.open (..);
    socket.bind (..);
    socket.async_receive_from (..);
    io_service.run ();	
    ... // ich mache ein paar erfolgreiche receives
    
    // in einem anderen thread:
    io_service.stop ();
    thread.join (); // stellt sicher, dass io_service.run zurückgekehrt ist
    io_service.reset ();
    
    socket.shutdown ( shutdown_both );
    socket.close ();
    
    io_service.run (); // und nochmal: merke, dass der socket nicht mal mehr geöffnet wurde
    
    // und hier bekomme ich doch wieder ein async_receive_from () Aufruf.
    

    Es wird also eine asynchrone Operation ausgeführt, obwohl ich den io_service zurückgesetzt habe und ebenfalls den socket runtergefahren. Und in der Doku für shutdown steht eindeutig:

    Disable sends or receives on the socket.

    http://www.boost.org/doc/libs/1_41_0/doc/html/boost_asio/reference/basic_datagram_socket/shutdown.html

    Natürlich kommt nur ein Fehler an:

    Der E/A-Vorgang wurde wegen eines Threadendes oder einer Anwendungsanforderung abgebrochen

    Ist das jetzt ein Bug oder hat noch jemand eine gute Idee?



  • Du musst shutdown + close aufrufen, solange die Eventloop vom io_service aktiv ist. Wenn Du die Eventloop abwürgst, dann können auch keine Handler mehr darin ausgeführt werden, und daher kann die von Dir gestartete asynchrone Operation auch nicht mehr auf das close() reagieren.

    Probiers mal so:

    boost::asio::io_service io_service;
    boost::asio::ip::udp::socket socket;
    ...
    socket.open (..);
    socket.bind (..);
    socket.async_receive_from (..);
    io_service.run ();   
    ... // ich mache ein paar erfolgreiche receives
    
    // in einem anderen thread:
    
    //shutdown und close in den Eventhandler hängen (besser noch die passende io_service-Instanz mit socket.get_io_service() besorgen)
    io_service.post(boost::bind(&udp::socket::shutdown, &socket, udp::socket::shutdown_both ));
    io_service.post(boost::bind(&udp::socket::close, &socket)); //der auf dem Socket aktive Handler kommt mit operation_aborted zurück
    
    io_service.post(boost::bind(&io_service::stop, &io_service)); //Aufruf von io_service::stop queuen, sonst kann es passieren, dass die oben eingereihten Handler nicht mehr ausgeführt werden
    thread.join (); // stellt sicher, dass io_service.run zurückgekehrt ist
    io_service.reset ();
    
    io_service.run (); // und nochmal: merke, dass der socket nicht mal mehr geöffnet wurde
    
    // und hier bekomme ich doch wieder ein async_receive_from () Aufruf.
    

    PS: Wieso beendest Du die Eventloop vom io_service überhaupt?



  • Guter Vorschlag, aber zuerst hatte ich das shutdown und close noch vor dem stop des io_service. Aber auch mit dem post habe ich das selbe Problem. Der Aufruf kommt nachdem ich alles runtergefahren habe.

    Ich fahre den runter, damit ich alles im Sinne eines reset wieder herstellen kann. Also den socket z.B auf einen anderen Port o.ä zu connecten.
    Das Problem ist halt, dass wenn nach dem runterfahren ein Funktionsaufruf kommt, dass intern ja noch auf einen Buffer zugegriffen wird, welcher aber nicht mehr gültig ist, weil ich den eben ebenfalls zurücksetzen (leeren) möchte.

    /EDIT:
    post ist ja sowieso ebenfalls dazu da den Aufruf asynchron zu machen. Er kehrt ja einafach sofort zurück.



  • drakon schrieb:

    Guter Vorschlag, aber zuerst hatte ich das shutdown und close noch vor dem stop des io_service. Aber auch mit dem post habe ich das selbe Problem. Der Aufruf kommt nachdem ich alles runtergefahren habe.

    Welcher Aufruf?

    Grundsätzlich: Du musst eine io_service -Instanz die eine aktive Eventloop hat nicht beenden, um neue Sockets hinzuzufügen oder zu entfernen oder zu schließen oder neu zu öffnen. Das kannst Du alles auch machen, während der io_service aktiv ist.

    io_service::post() synchronisert die Aufrufe vor allem in die Eventqueue vom io_service. Es geht darum, die auszuführenden Handler zu queuen und sie sequentialisieren. Es geht nicht darum, non-blocking Calls zu bekommen (dafür kann man es natürlich ebenfalls benutzen).

    Im Quelltext oben wird also erst socket::shutdown() , dann socket::close() und dann io_service::stop() in die Eventqueue vom io_service geschoben. Die Handler werden dann nach FIFO-Strategie ausgeführt, und zwar im Konext des io_service-Threads.

    Wenn Du io_service::stop() aufrufst, killst Du den Thread, der die Eventhandler abarbeitet. Die Queue darin kann also nicht mehr geleert werden. io_service::reset() leert die Queue nicht, sondern setzt nur ein paar Flags zurück, ähnlich wie clear() bei den Streams aus der Standardlib.
    Dementsprechend werden noch eingereihte Handler ausgeführt, wenn Du erneut io_service::run aufrufst. Das ist dann der Effekt "dass der Aufruf kommt, nachdem Du alles heruntergefahren hast" (das socket.close() schlägt durch und der Handler kommt mit operation_aborted zurück).

    Vielleicht sollte ich mal einen Artikel zu boost.asio schreiben...


  • Administrator

    drakon schrieb:

    Ich fahre den runter, damit ich alles im Sinne eines reset wieder herstellen kann. Also den socket z.B auf einen anderen Port o.ä zu connecten.

    Darf ich fragen, wieso du das Socket wiederverwenden willst? Bei solchen Dingen erstelle ich lieber gleich was neues. Der Overhead ist minimal, dafür hat man eben keine solchen Probleme. Ist das Gleiche wie mit den In-/Output Streams der Standardbibliothek. Erspart einem eine Menge an Ärger.

    Tachyon schrieb:

    Vielleicht sollte ich mal einen Artikel zu boost.asio schreiben...

    http://www.c-plusplus.net/forum/viewtopic-var-t-is-213365.html

    Grüssli



  • Tachyon schrieb:

    Welcher Aufruf?

    Der asynchrone Aufruf, dass etwas beendet wurde. (mit der obigen Meldung).

    Grundsätzlich: Du musst eine io_service -Instanz die eine aktive Eventloop hat nicht beenden, um neue Sockets hinzuzufügen oder zu entfernen oder zu schließen oder neu zu öffnen. Das kannst Du alles auch machen, während der io_service aktiv ist.

    io_service::post() synchronisert die Aufrufe vor allem in die Eventqueue vom io_service. Es geht darum, die auszuführenden Handler zu queuen und sie sequentialisieren. Es geht nicht darum, non-blocking Calls zu bekommen (dafür kann man es natürlich ebenfalls benutzen).

    Im Quelltext oben wird also erst socket::shutdown() , dann socket::close() und dann io_service::stop() in die Eventqueue vom io_service geschoben. Die Handler werden dann nach FIFO-Strategie ausgeführt, und zwar im Konext des io_service-Threads.

    Wenn Du io_service::stop() aufrufst, killst Du den Thread, der die Eventhandler abarbeitet. Die Queue darin kann also nicht mehr geleert werden. io_service::reset() leert die Queue nicht, sondern setzt nur ein paar Flags zurück, ähnlich wie clear() bei den Streams aus der Standardlib.
    Dementsprechend werden noch eingereihte Handler ausgeführt, wenn Du erneut io_service::run aufrufst. Das ist dann der Effekt "dass der Aufruf kommt, nachdem Du alles heruntergefahren hast" (das socket.close() schlägt durch und der Handler kommt mit operation_aborted zurück).

    Ok, das macht Sinn. Das konnte ich leider aus der Doku nicht lesen.. Woher hast du denn das?

    Danke, ich habe vorhin anscheindend das stop nicht in die post eingereiht gehabt. Jetzt gehts.

    Dann ist demfall die Doku einfach nicht zu gebrauchen. Denn hier steht rein gar nichts von einem io_service, also gehe ich davon aus, dass das gemacht wird, was da steht.. 🙄

    @Dravere:
    Ich weiss nicht recht. Es ist denke ich mehr aus Prinzip, weil ich dachte, dass es gehen sollte und jetzt will ichs so haben und kein Workaround. 😉
    Das zurücksetzen usw. läuft ja einwandfrei. Es war halt eben lediglich das mit dem queuing der Calls.

    Danke auf jeden Fall nochmal an dich Tachyon!



  • drakon schrieb:

    Dann ist demfall die Doku einfach nicht zu gebrauchen. Denn hier steht rein gar nichts von einem io_service, also gehe ich davon aus, dass das gemacht wird, was da steht.. 🙄

    Zugegeben: Die Doku ist manchmal etwas arg dünn.

    socket::shutdown() sorgt allerdings erstmal nur dafür, dass man Senden, Empfangen oder beides für einen Socket ausknipsen kann. Dadurch kommt es nicht zu dem "Fehler".

    Der Fehler ist auch kein Fehler, sondern das korrekte Verhalten von socket::close() :

    basic_datagram_socket::close schrieb:

    This function is used to close the socket. Any asynchronous send, receive or connect operations will be cancelled immediately, and will complete with the boost::asio::error::operation_aborted error.

    Der E/A-Vorgang wurde wegen eines Threadendes oder einer Anwendungsanforderung abgebrochen ist die Win-API Meldung zu opearation_aborted .

    drakon schrieb:

    Ok, das macht Sinn. Das konnte ich leider aus der Doku nicht lesen.. Woher hast du denn das?

    Die Sache mit dem io_service::post() habe ich aus der Doku. In den Beispielen ist das ein häufig verwendetes Pattern. Außer io_service ist fast keine asio-Klasse threadsafe. Deshalb müssen potentiell konkurrierende Aufrufe von Memberfunktionen auf Objekte dieser Klassen synchronisiert werden. Guck Dir z.B. mal die Close-Funktion aus dem Chat-Client Beispiel an.

    Das mit der Queue und der Arbeitsweise der asynchronen Operationen habe ich aus der boost.asio-Übersicht. Da wo das Proactor-Pattern beschrieben wird.



  • Naja. Ich habe das dort eben so verstanden, dass innerhalb von close die Funktionen mit dem error aufgerufen werden sollten. Das verstehe ich unter immediately. Da steht ja nichts davon, dass die Aufrufe in die queue eingereiht werden..

    Etwas ist mir aber nicht ganz klar. Warum synchronisiert du die Aufrufe von socket::shutdown und socket::close? - Es reicht ja dafür zu sorgen, dass io_service::stop erst am Ende ausgeführt wird. Habs mal getestet und funktionieren tuts auch.

    Und sehe ich das richtig, dass socket::cancel hier wirklich sofort die Handler beendet und somit eher das Mittel der Wahl wäre? (ich habe XP, darum kann ichs nicht testen).

    Ja. Die Doku ist mir wirklich zu mager. Aber abgesehen von der boost Seite und den Beispielen hast du auch nichts anderes mehr gefunden? (Ich habe auch mal ein wenig gesucht und hatte einfach das Gefühl, dass asio kaum wer braucht. ;))



  • Handler werden grundsätzlich über den Dispatacher vom io_service ausgeführt.

    Soll heißen: Wenn Du einen Handler über irgendeine asynchrone Operation in die Queue schiebst, wird dieser erst ausgeführt, wenn Du run() , run_one() , poll() oder poll_one() für das benutzte io_service -Objekt aufrufst.

    Due Aufrufe von shutdown() und close() müssen synchronisiert werden, weil sie sonst potentiell parallel zur Abarbeitung z.B. eines async_receive passieren können. Wenn gerade ein empfangenes Datagramm bearbeitet wird (vom Socket), und Du gleichzeitig z.B. close() auf den Socket aufrufst, dann hast Du eine klassische Race Condition. Die geteilte Resource ist dabei der Socket selbst und es greifen zwei Threads drauf zu (der vom io_service der gerade das async_receive bearbeitet, und der, der close() aufruft).

    Wieso kannst Du es mit Win-XP nicht testen? -> Achja, cancel() geht da ja nicht.



  • Naja, wegen der mangelnden Unterstützung, die in den Remarks vermekt ist.



  • Tja.. Tachyon.. Ich befürchte, dass es doch nicht so einfach war. 😉

    Ich habe das selbe Fehlerbild, aber jetzt in einer einfacheren Situation bekommen.

    Wenn ich nämlich jetzt gar keine receives mehr empfange und gleich resetten will, dann funktioniert das mit dem posten von shutdown und close nicht. Aus irgendeinem Grund kommt manchmal die Abbruch Meldung trotzdem erst wenn der service wieder gestartet ist (es war also ein Handler nach io_service::stop drin..). Das konnte ich bis jetzt nicht beobachten, wenn ich die calls einfach ohne post gemacht habe.

    // böse:
    //io_service_.post ( boost::bind ( &udp::socket::shutdown, &socket_, udp::socket::shutdown_both ) );
    //io_service_.post ( boost::bind ( &udp::socket::close, &socket_ ) );
    
    // anscheinend gut:
    socket_.shutdown ( udp::socket::shutdown_both );
    socket_.close ();
    
    io_service_.post ( boost::bind ( &boost::asio::io_service::stop, &io_service_) );
    
    run_thread_->join ();
    io_service_.reset ();
    

    Dabei müssten ja die die beiden calls tatsächlich vor dem io_service::stop kommen.. Für mich macht das ganze irgendwie keinen Sinn mehr.. 😕



  • Sollte es nicht so laufen?

    1. shutdown socket (nur receive seite)
    2. receive handler wird aufgerufen mit error eof, bzw. bytes_transferred == 0
    3. close socket im receive handler

    Oder bin ich irgendwie ganz falsch gewickelt.

    Simon


  • Administrator

    Kannst du mal ein minimales Beispiel zeigen, wo der Fehler passiert? Wenn ich Zeit finde, möchte ich das heute mal durchtesten. 😉

    Grüssli

    PS: Es kam schon wieder eine neue Boost Version raus ... 😮



  • @theta:
    Da tachyon noch nichts gesagt hat und in der boost Doku nix davon steht, gehe ich davon aus, dass es schon so geht..

    @dravere:
    Ja, werde ich wahrscheinlich heute Abend noch machen. Im Moment habe ich ein paar andere Sachen im Kopf. 😉



  • drakon schrieb:

    Wenn ich nämlich jetzt gar keine receives mehr empfange und gleich resetten will, dann funktioniert das mit dem posten von shutdown und close nicht.

    Das sollte eigentlich immer funktionieren. Voraussetzung dafür ist allerdings, dass Du einen io_service mit aktivem Eventprozessor hast. Wenn nicht, dann tut post() genau gar nichts.

    drakon schrieb:

    Aus irgendeinem Grund kommt manchmal die Abbruch Meldung trotzdem erst wenn der service wieder gestartet ist (es war also ein Handler nach io_service::stop drin..). Das konnte ich bis jetzt nicht beobachten, wenn ich die calls einfach ohne post gemacht habe.

    Das darf so nicht sein. Ich glaube, Du solltest doch mal deutlich mehr Code zeigen. Vor allem den, wo Du die asynchronen Operationen startest.

    // anscheinend gut:
    socket_.shutdown ( udp::socket::shutdown_both ); 
    socket_.close ();
    //nein, sehr schlecht. sockets sind nicht threadsafe, und Du greifst mit zwei
    //Threads drauf zu (der aus dem Eventprozessor, und der, der close() aufruft-
    

    Ich stelle nochmal die Frage:
    Wieso beendest Du den io_service ? Du kannst auch bei laufendem Eventprozessor die sockets schließen und wieder neu öffnen. Noch besser wäre, wie hier bereits gesagt wurde, das Socketobjekt komplett neu zu erstellen.



  • Das der Zugriff da nicht synchronisiert ist, ist mir klar, allerdings habe ich, wie gesagt den Fehler so nicht reproduzieren können, daher "anscheinend gut". 😉

    Das ich da den io_service neu starte muss wohl nicht sein, ja, aber ich dachte halt zuerst eben, dass es nötig seie, oder zumindest gut ginge. Im Moment brauche ich das vor allem, um sicher zu gehen, dass auch alle Handler durch sind und warte daher bis der Thread zurück kommt und starte ihn neu. Ich denke, dass man das mit io_service::poll auch erreichen kann, aber jetzt will ich zuerst das hier zum laufen kriegen. 😉

    #include <iostream>
    #include <vector>
    #include <string>
    
    #define _WIN32_WINNT 0x0501
    
    #include <boost/asio/io_service.hpp>
    #include <boost/asio/ip/udp.hpp>
    #include <boost/asio/buffer.hpp>
    
    #include <boost/thread.hpp>
    #include <boost/system/error_code.hpp>
    #include <boost/bind.hpp>
    
    using boost::asio::ip::udp;
    
    boost::asio::io_service io_service;
    std::vector<char> buffer ( 64 );
    std::vector<char> send_buffer  ( 64 );
    boost::asio::ip::udp::socket socket_  ( io_service );
    
    boost::asio::ip::udp::endpoint remote_endpoint ;
    std::auto_ptr<boost::thread> run_thread;
    
    void start ();
    void received ( const boost::system::error_code& error, std::size_t bytes_received );
    void cleanup ();
    void restart ( unsigned int size );
    
    void receiving ()
    {
    	socket_.async_receive_from ( boost::asio::buffer ( buffer ), remote_endpoint, &received );
    }
    
    void sent ( const boost::system::error_code& error, std::size_t bytes_sent )
    {
    	receiving ();
    }
    
    void received ( const boost::system::error_code& error, std::size_t bytes_received )
    {
    	if ( !error )
    	{
    		std::string answer = "tag";
    		send_buffer = std::vector<char> ( answer.begin (), answer.end () );
    
    		socket_.async_send_to ( boost::asio::buffer ( send_buffer ), remote_endpoint, &sent );
    	}
    	else
    		std::cout << "error - received " << error.message () << "\n";
    }
    
    void run ( boost::asio::io_service* s )
    {
    	s->run ();
    }
    
    void restart ( unsigned int size )
    {
    	cleanup ();
    
    	// auskommentieren, damit die Meldung ausgegeben wird
    	buffer = std::vector<char> ( size );
    
    	start ();
    }
    
    void start ()
    {
    	socket_.open ( udp::v4 () );
    	socket_.bind ( udp::endpoint ( udp::v4 (), 33333) );
    
    	receiving ();
    
    	run_thread.reset ( new boost::thread ( boost::bind ( &run, &io_service ) ) );	
    }
    
    void cleanup ()
    {
    	// böse
    	io_service.post ( boost::bind ( &udp::socket::shutdown, &socket_, udp::socket::shutdown_both ) );
    	io_service.post ( boost::bind ( &udp::socket::close, &socket_ ) );
    
    	// anscheinend ok
    	//socket_.shutdown ( udp::socket::shutdown_both );
    	//socket_.close ();
    
    	io_service.post ( boost::bind ( &boost::asio::io_service::stop, &io_service) );
    
    	run_thread->join ();
    	io_service.reset ();
    
    	std::cout << "cleanup finished\n";
    }
    
    /*
    	Korrekterweise sollte zuerst "server" ausgegeben werden, dann bei der eingabe von "quit" sollte die error Meldung ausgegeben werden
    	und erst dann "cleanup finished". Allerdings kommt es vor, dass bei den post-Varianten zuerst "cleanup finished" kommt, dann "server"
    	und erst dann kommt die error Meldung, dass eine Verbindung unterbrochen wurde.
    	Respektive es kommt eine Laufzeitmeldung, dass vector nicht dereferenziert werden kann, was daher kommt, dass der buffer in 
    	restart invalidiert wird, was kein Problem ist, wenn die Meldung noch innerhalb von cleanup vorkommt.
    
    */
    int main ()
    {
    	start ();
    
    	for (int i = 0; i < 2; ++i)
    	{
    		std::cout << "server\n";
    
    		for (;;)
    		{
    			std::string c;
    			std::cin >> c;
    			if ( c == "quit" )
    				break;
    		}
    
    		restart ( 64 );
    	}
    }
    

    Und so siehts dann manchmal aus:
    http://dl.dropbox.com/u/1716912/udp_error.jpg


  • Administrator

    Was heisst manchmal? Habe es nun 100 Mal getestet, konnte das Verhalten nicht reproduzieren:
    Win7 64 Bit, MSVC 9.0 (2008), Boost 1.42.0 (ganz frisch geholt).

    Wobei bei 1.42.0 anscheinend ein paar Bugs behoben wurden, welche womöglich mit dem Problem zusammenhängen könnten, bin mir aber nicht ganz sicher:
    http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/history.html

    Welche Version verwendest du denn? Dann kann ich es allenfalls noch mit der probieren, müsste ich allerdings auch zuerst noch kompilieren.

    Die einzige Sache, wo ich nicht so genau weiss, wie das eigentlich aussieht und mir hier auffällt: Wie sieht die Synchronisation von std::cout über mehrere Threads aus? Könnte es ein Problem sein, dass da eine Nachricht im Puffer verbleibt und dann eine andere Nachricht zuerst auftaucht, obwohl sie erst später geschrieben wurde? Kenne mich da zu wenig aus. Vielleicht noch bei jeder Ausgabe std::endl verwenden und ein std::clock Stempel dazu 😉

    Grüssli



  • Also ich kann das sehr gut nachvollziehen, wenn ich einfach sobald das Fenster aufgeht "quit" mache. Dann brauche ich da 2-3x zu versuchen und ich habe einen Fehler.

    Ich habe die 1.41 Version.
    Kann sein, dass was behoben wurde, habe was gelesen in der changelog:

    Fixed a problem with the lifetime of handler memory, where Windows needs the OVERLAPPED structure to be valid until both the initiating function call has returned and the completion packet has been delivered.

    Das Problem bei mir hat ja was mit dem memory zu tun, allerdings wird bei mir ja nicht der Speicher direkt falsch behandelt, sondern eben nur der Handler kommt zu spät.

    Und nein. An cout liegst nicht, wenn ich kriege den Fehler ja aufgrund von einem Speicherzugriffsfehler in vector. Die Ausgabe habe ich nur da, damit man es besser sehen kann.



  • Der Code hat einiger Fehler.

    Nur, um mal eine potentielle Quelle zu nennen:

    void restart ( unsigned int size )
    {
        //hier drin werden Dinge über post() asynchron gestartet
        cleanup ();
    
        //das hier läuft aber in einem anderen Thread
        buffer = std::vector<char> ( size );
    
        start ();
    }
    

    Probier es mal so:

    #include <iostream>
    #include <vector>
    #include <string>
    
    #define _WIN32_WINNT 0x0501
    
    #include <boost/asio/io_service.hpp>
    #include <boost/asio/ip/udp.hpp>
    #include <boost/asio/buffer.hpp>
    
    #include <boost/thread.hpp>
    #include <boost/system/error_code.hpp>
    #include <boost/bind.hpp>
    
    using boost::asio::ip::udp;
    
    boost::asio::io_service io_service;
    //so bleibt der Eventprozessor aktiv, auch wenn nichts zu tun ist
    boost::asio::io_service::work worker(io_service);
    
    std::size_t bufferSize = 64;
    std::vector<char> buffer ( bufferSize );
    std::vector<char> send_buffer  ( bufferSize );
    
    boost::asio::ip::udp::socket socket_(io_service);
    
    boost::asio::ip::udp::endpoint remote_endpoint ;
    //muss nicht geheapt werden
    boost::thread run_thread;
    
    void initSocket();
    void closeSocket();
    void startServer();
    void nextCycle();
    void handleReceive(boost::system::error_code const & error, std::size_t bytesReceived);
    void handleSend(boost::system::error_code const & error, std::size_t bytesReceived);
    void handleError(boost::system::error_code const & error);
    void restart(std::size_t size);
    
    //socket initialisieren
    void initSocket()
    {
        socket_.open (udp::v4());
        socket_.bind (udp::endpoint(udp::v4(), 33333));
    }
    
    //socket schliessen
    void closeSocket()
    {
        socket_.shutdown(udp::socket::shutdown_both);
        socket_.close();
    }
    
    //asynchrones Zeugs anwerfen
    void startServer()
    {
        initSocket();
        nextCycle();
    }
    
    //ein Zyklus besteht aus receive->send->...
    //nextCycle() leitet so einen Zyklus ein
    void nextCycle()
    {
        socket_.async_receive_from
            ( boost::asio::buffer(buffer)
            , remote_endpoint
            , &handleReceive ); 
    }
    
    void handleReceive(boost::system::error_code const & error, std::size_t bytesReceived)
    {
        if(error)
        {
            //bei einem Fehler wollen wir was tun!
            handleError(error);
        }
        else
        {
            char const answer[] = "tag";
            send_buffer.assign(answer, answer + sizeof(answer));
            socket_.async_send_to
                ( boost::asio::buffer(send_buffer)
                , remote_endpoint
                , &handleSend ); 
        }
    }
    
    void handleSend(boost::system::error_code const & error, std::size_t bytesReceived)
    {
        if(error)
        {
            //bei einem Fehler wollen wir was tun!
            handleError(error);
        }
        else
        {
            nextCycle();
        }
    }
    
    void handleError(boost::system::error_code const & error)
    {
        //hier ist einer der Handler sicher mit einem Fehler
        //zurueckgekommen
        std::cerr << "Error: " << error.message() << '\n';
        //wenn der Fehlercode "abbruch" sagt (close()):
        if(error == boost::asio::error::operation_aborted)
        {
            //Buffer setzen
            buffer.assign(bufferSize, 0);
            //Socket neu oeffnen
            initSocket();
            //neuen Zyklus einleiten
            nextCycle();
        }
    }
    
    void restart(std::size_t size)
    {
        bufferSize = size;
        //Socket schliessen
        socket_.get_io_service().post(&closeSocket);
    }
    
    int main ()
    {
        startServer();
        run_thread.swap(boost::thread(boost::bind(&boost::asio::io_service::run, &io_service)));
    
        for (int i = 0; i < 4; ++i)
        {
            std::cout << "server\n";
    
            for (;;)
            {
                std::string c;
                std::cin >> c;
                if ( c == "quit" )
                    break;
            }
            restart ( 64 );
        }
    }
    


  • Hmm. Danke für das Beispiel.
    Werds mir bestimmt genauer anschauen.

    Aber wo meinst du sind sonst noch Fehler?

    Und irgendwie will ich schon noch wissen, was an obigem Code explizit den Fehler verursacht. Wie du ja sagts müssten die Handler, die per post aufgerufen werden der Reihe nach korrekt aufgerufen werden, was ja anscheinend nicht wirklich passiert.

    Potentiell glaube ich dir schon, dass es Fehler drin hat, aber was ruft mir denn jetzt den Fehler hervor? (konntest du ihn überhaupt auch mal nach vollziehen?)


Anmelden zum Antworten