Boost ASIO listen-mode unterbrechen



  • mein gott ist das kompliziert da progg ich lieber winsock



  • Hallo,

    Das finde ich auch nicht so schön.
    Da doch auch das Beenden eines Prozesses immer dazugehört.

    Vielleicht dazu eine kleine andere Frage.
    Lohnt sich ASIO vielleicht nur für kleinere Projekte?
    Wobei ich klein jetzt nicht näher definieren möchte.

    Viele Grüße
    Thomy



  • ASIO ist nicht an sich schlecht.
    ASIO ist IMO auch nicht nur für kleine Projekte geeignet.

    ASIO ist einfach nur unter bestimmten Bedingungen etwas umständlich zu verwenden. Und oft sind die Möglichkeiten wie man etwas einfach und korrekt mit ASIO machen kann, nicht ganz offensichtlich. Auf Grund der Lernkurve die sich dadurch ergibt, denke ich sogar, dass ASIO für kleine Projekte eher schlechter geegnet ist, als für grosse Projekte.

    BTW: wenns nur um das Beenden des ganzen Programms geht, gibt's noch einen halbwegs einfachen Weg: io_service::stop() .

    Das führt dazu dass io_service::run() in allen Threads zurückkommt.

    Wenn du danach das io_service Objekt zerstörst, sorgt der Dtor von io_service dafür, dass auch alle "ausständigen" Handler zerstört werden.
    (Sie werden aber NICHT "aufgerufen", sondern nur regulär "zerstört". D.h. der Dtor läuft, der operator () aber nicht!)

    Und das Zerstören der Handler wiederum kannst du verwenden, um "sauberzumachen" (Log Eintrag schreiben, Socket kontrolliert zumachen, Resourcen freigeben etc.).

    Ein Pattern das nicht ganz unüblich ist, ist die Sessions nur implizit am Leben zu halten, indem man boost::shared_ptr in die Handler mit reinbindet ( boost::bind(&Session::HandlerFunction, sharedPtrToSession, ...) ).
    Dadurch wird ein Session Objekt automatisch zerstört, sobald es keinen "pending handler" mehr gibt. D.h. wenn man io_service::stop() verwendet, und danach das io_service Objekt zerstört, werden dabei auch alle Sessions zerstört.

    p.S.:

    Ich habe im Code auch ein paar Stellen entdeckt, wo es passieren kann, dass Handler nicht aufgerufen werden, sondern nur zerstört. Ich weiss leider nichtmehr welche Funktionen das waren, aber es gibt Fälle wo es vorkommen kann.
    Konkret wurde io_service::post verwendet, um den Handler "zu posten". Wenn nun in io_service::post z.B. ein std::bad_alloc fliegt ( io_service::post muss u.U. Speicher anfordern, d.h. das kann passieren), wird der Handler nie aufgerufen.

    Dass er zerstört wird sollte dagegen immer sichergestellt sein. (Ich hab da zwar auch nen Bug gefunden, wo Handler nicht freigegeben wurden, aber den hat der Chris Kohlhoff sofort gefixt nachdem ich ihn gemeldet habe, ist in 1.39.0 also behoben. Wie viele andere solche Bugs es noch gibt kann ich natürlich nicht sagen :D).

    Worauf ich damit hinaus will ist einfach...
    Programme die die ASIO verwenden, sollten darauf ausgelegt sein, mit Fällen klarzukommen, wo ein Handler nie ausgeführt wird, sondern nur zerstört. Da der Zustand des Streams in dem Fall dann unbekannt ist, sollte man diese Fälle behandeln, in dem man die Session abbricht.

    Und wenn man das macht, kann man auch io_service::stop() wie beschrieben verwenden, wenn man alle Connections schliessen will, um das Programm zu beenden.

    p.p.S.: Sorry dass das schon wieder so lange geworden ist 🙂 Mich kurz fassen ist nicht eine meiner Stärker 🙂



  • Danke, im Gegenteil ausführich war lecker. 😋

    Das Thema "Lernerei" stimmt auf jeden Fall, nur wenn man davon mal absieht,
    kann man mit ASIO ja eigentlich ganz fix und direkt mit einem "Anfangsdesign" ein neues Programm entwerfen.
    Ich finde mich durch ASIO halt nur durch solche Eigenheiten ein wenig eingeschränkt,
    was ich mit einem simplen Socket-Wrapper, der natürlich nicht dieses gewisse "Anfangsdesign" bietet, nicht bin.

    Für einen kleinen Shell-Befehl, der sich connected, und vorausgesetzt, man kennt sich mit ASIO aus,
    würde ich ASIO empfehlen. (Notfalls killen 😃 )
    Aber bei einem aufwendigen Server-Prozess, der vllt. zwischendurch Clients, die in der Listen-Schlange stehen,
    rauswirft, nur damit ein anderer Listen-Port geschlossen werden kann, finde ich nicht so elegant.

    Okay, lassen wir es vielleicht hierbei.
    Wenn ich gefragt werde, ob ich ASIO empfehlen kann, sage ich: "Hää? ASIO?"

    Viele Grüße und Danke
    Thomy

    PS: "Anfangsdesign"?? -Mir ist nichts besseres eingefallen. Man wird halt durch das ASIO-Konzept geführt.



  • hustbaer: du scheinst da ja richtig den Durchblick zu haben - lässt du uns teilhaben in Form eines Magazinartikels? 🙂 Sowas fehlt noch... Die Tutorials auf der offiziellen Seite find ich etwas mau.



  • pumuckl schrieb:

    hustbaer: du scheinst da ja richtig den Durchblick zu haben - lässt du uns teilhaben in Form eines Magazinartikels? 🙂 Sowas fehlt noch... Die Tutorials auf der offiziellen Seite find ich etwas mau.

    Ja, das wäre grandios. 😋

    Kennt eigentlich jemand das "Proposal to add a networking library to standard library for TR2"? Das ist ja quasi ASIO und es werden ein paar Dinge etwas besser beschrieben, als in der ASIO Doku.



  • pumuckl schrieb:

    hustbaer: du scheinst da ja richtig den Durchblick zu haben - lässt du uns teilhaben in Form eines Magazinartikels? 🙂 Sowas fehlt noch... Die Tutorials auf der offiziellen Seite find ich etwas mau.

    Jain.

    Ich hab' angefangen einen "XML-Schnippel-Hin-Und-Her-Schicker" (aka. "Application-Server") mit der ASIO zu programmieren, und bin dabei über einige Dinge gestolpert, die ich mir dann etwas genauer angesehen habe. Dort bin ich auch über das Handler-Leak in der OpenSSL Anbindung gestolpert. Den vollen 100%igen Durchblick hab ich aber auch noch nicht.

    Wenn ich da nen Artikel schreiben soll, wäre es aber auf jeden Fall hilfreich, ein paar Fragen/Vorschläge zum Inhalt zu bekommen. Was jetzt aber nicht heisst dass ich das in nächster Zeit machen werde, das muss ich mir noch überlegen. Hab so schon immer zu wenig Zeit 🙂



  • Tachyon schrieb:

    Kennt eigentlich jemand das "Proposal to add a networking library to standard library for TR2"? Das ist ja quasi ASIO und es werden ein paar Dinge etwas besser beschrieben, als in der ASIO Doku.

    Kannte ich noch nicht. Dass es sich dabei im grossen und ganzen um die ASIO handelt, ist aber nicht weiter verwunderlich, wenn man sieht, dass es von Chris Kohlhoff geschrieben wurde 🙂

    BTW: http://www.think-async.com/ und http://blog.think-async.com/



  • hustbaer schrieb:

    Jain.

    Ich hab' angefangen einen "XML-Schnippel-Hin-Und-Her-Schicker" (aka. "Application-Server") mit der ASIO zu programmieren, und bin dabei über einige Dinge gestolpert, die ich mir dann etwas genauer angesehen habe. Dort bin ich auch über das Handler-Leak in der OpenSSL Anbindung gestolpert. Den vollen 100%igen Durchblick hab ich aber auch noch nicht.

    Das die wirklich guten Leute immer so bescheiden sind 😉

    Gruß
    WAR][FIRE



  • Dravere schrieb:

    Wie ich es damals im Thread gesagt hatte und hustbaer es in der Liste aufgeführt hat, mach die Sache asynchron, dann geht es. Hier ein sehr kurzes Beispiel:

    #include <boost/asio.hpp>
    #include <boost/bind.hpp>
    #include <boost/thread.hpp>
    #include <boost/date_time/posix_time/posix_time.hpp>
    
    void acceptHandler(boost::system::error_code const& /*error*/)
    {
        // whatever
    }
    
    int main()
    {
        typedef boost::asio::ip::tcp::socket Socket;
        typedef boost::asio::ip::tcp::acceptor Acceptor;
    
        unsigned short const PORT = 1234;
    
        boost::asio::io_service service;
        Acceptor::endpoint_type endpoint(Acceptor::protocol_type::v4(), PORT);
        Acceptor acceptor(service, endpoint);
    
        Socket peer(acceptor.io_service());
        acceptor.async_accept(peer, &acceptHandler);
    
        boost::thread t1(boost::bind(&boost::asio::io_service::run, &service));
    
        boost::this_thread::sleep(boost::posix_time::seconds(5));
    
        service.post(boost::bind(&Acceptor::close, &acceptor));
    
        t1.join();
    
        return 0;
    }
    

    Ich möchte darauf hinweisen, dass man mit einer neueren Boost Version zum Beispiel boost::thread einfacher starten kann. Ich arbeite hier noch mit einer bisschen älteren Version. Auch habe ich den Code ziemlich schnell hingeschrieben, da es nur mein Ziel ist, die Idee zu vermitteln.
    Ganz wichtig ist der service.post(...) Aufruf. Der acceptor ist nicht Thread sicher, also muss es der gleiche Thread sein, welcher die close Funktion aufruft, wie derjenige welcher auf ankommende Verbindungen wartet. Dies wird mit dieser Funktion garantiert, da sie den Funktionsaufruf auch asynchron macht und intern in eine Art von Nachrichtenschleife setzt.

    Der Thread t1 wird übrigens beendet, wenn alle asynchronen Funktionsaufrufe, bzw. man auch könnte sagen alle Nachrichten, abgearbeitet wurden.

    Grüssli

    Hallo Dravere!

    Funktioniert folgende Funktion aus deinem Code:

    acceptor.async_accept(peer, &acceptHandler);
    

    Auch in Klassen`?
    acceptHandler ist bei bei mir eine Methoder in der Klasse foo.

    Beim Aufruf im Konstruktor dieser Klasse:

    acceptor.async_accept(peer, &foo::acceptHandler);
    

    kommt folgender Fehler:

    Error	3	error C2064: term does not evaluate to a function taking 1 arguments	...boost_1_39_0\boost\asio\detail\bind_handler.hpp	39	Application
    

    In der Datei bind_handler.hpp sitzt der Fehler dann in Zeile 39.

    Auszug:

    //
    // bind_handler.hpp
    // ~~~~~~~~~~~~~~~~
    //
    // Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
    //
    // Distributed under the Boost Software License, Version 1.0. (See accompanying
    // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
    //
    
    #ifndef BOOST_ASIO_DETAIL_BIND_HANDLER_HPP
    #define BOOST_ASIO_DETAIL_BIND_HANDLER_HPP
    
    #if defined(_MSC_VER) && (_MSC_VER >= 1200)
    # pragma once
    #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
    
    #include <boost/asio/detail/push_options.hpp>
    
    #include <boost/asio/detail/handler_alloc_helpers.hpp>
    #include <boost/asio/detail/handler_invoke_helpers.hpp>
    
    namespace boost {
    namespace asio {
    namespace detail {
    
    template <typename Handler, typename Arg1>
    class binder1
    {
    public:
      binder1(const Handler& handler, const Arg1& arg1)
        : handler_(handler),
          arg1_(arg1)
      {
      }
    
      void operator()()
      {
        handler_(arg1_);                      //Zeile 39. Hier sitzt der Fehler von oben.
      }
    
      void operator()() const
      {
        handler_(arg1_);
      }
    
    //private:
      Handler handler_;
      Arg1 arg1_;
    };
    

    Ich finde leider keine Lösung für das Problem. Evtl. lässt sich das ja leicht erklären 🙂

    Gruß
    WAR][FIRE


  • Administrator

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

    Am besten löst du es über Boost.Bind:

    struct Foo
    {
      void acceptHandler(boost::system::error_code const& /*error*/)
      {
      }
    };
    
    // ...
    
    Foo foo;
    acceptor.async_accept(peer, boost::bind(&Foo::acceptHandler, &foo, _1));
    

    Man kann so ein Bind auch innerhalb der Klasse machen und dann this nehmen. Kannst du übrigens auch in allen Tutorials und Beispielen von Asio finden.

    Grüssli



  • Danke Dravere!

    Jetzt funktioniert bei mir alles einwandfrei! 🙂

    Gruß
    WAR][FIRE


Anmelden zum Antworten