boost.asio eigene IO-Erweiterung



  • Hi Freaks,

    Es ist schon eine Weile her, dass ich mich das erste Mal mit boost.asio beschäftigt habe. Irgendwie hatte ich das Gefühl, dass die Lib genau das ist was ich für mein eigenes asynchrones IO brauche - und ich hatte selber schon vorher Implementierungen auf Basis des Proactor-Patterns realisiert.
    Aber beim Lesen der Doku kam mehr und mehr Frust auf.
    Zunächst findet man dort neue Bezeichner wie io_service, service, IO-Objekt u.a. und nirgendwo einen Hinweis welche Teile z.B. des Proactor-Pattern die einzelnen Klassen implementieren. Nur sehr kurze Sätze, für was die einzelne Klasse verantwortlich zeichnen.

    Jetzt ist es mal wieder soweit - aus aktuellem Anlass, dass boost.asio eine Option sein könnte. Ich möchte nun genau KEINE Network-Implementierung, sondern mir liegt eine kleine Lib für das Schreiben&Lesen auf dem CAN-Bus vor, die ich gerne mit asio kapseln würde. Die API, um die es geht besteht im wesentlichen aus
    canOpen
    canWrite // schreibt ein Paket auf den Bus
    canRead // liest ein Paket (blockierend!)
    canClose

    Ohne dieses Tutorial (Danke an Boris Schäling) hätte ich nicht heraus bekommen, wie ich eine eigene IO-Lib in boost.asio integrieren kann. Aber es bleiben noch viele Fragen offen. Dummerweise betreffen alle Beispiele entweder Networking oder Timer. Selbst für das Serial-IO, was meiner Anwendung wohl am nächsten kommt, gibt es kein Beispiel. Gleiches gilt für vieles, was man im Netz findet, inklusive dem Blog des Autors.

    Im wesentliche sind mir z.Zt. zwei Dinge unklar
    - der canRead muss nach dem Eintreffen einer Nachricht immer wieder aufgesetzt werden - also ein erneutes async_read muss 'automatisch' aufgerufen werden. Wer von Service, ServiceImpl, WaitungOperation bis Client wäre dafür zuständig? Oder besser: gibt es eine Möglichkeit, dass boost.asio die WaitingOperation wieder selbst aktiviert?
    - wenn ein Paket am Bus empfangen wird, muss ich es abhängig vom Inhalt dispatchen. Wer ist dafür zuständig? Ich könnte mir auch eine Lösung mit mehr als einem Handler vorstellen. Macht das Sinn?

    natürlich kann ich für beide Dinge Lösungen 'irgendwo' implementieren. Aber wie ist es letztlich gedacht. Was ist sinnvoll?
    Ist es überhaupt sinnvoll, boost.asio für so eine Anwendung zu nutzen. Das einzige was ich i.A. sehe, was mir boost.asio wirklich abnimmt, ist der io_service mit seiner post- Funktionalität.

    Gruß
    Werner



  • Werner Salomon schrieb:

    [...]- der canRead muss nach dem Eintreffen einer Nachricht immer wieder aufgesetzt werden - also ein erneutes async_read muss 'automatisch' aufgerufen werden. Wer von Service, ServiceImpl, WaitungOperation bis Client wäre dafür zuständig? Oder besser: gibt es eine Möglichkeit, dass boost.asio die WaitingOperation wieder selbst aktiviert?[...]

    Also die Implementierung stellt eigentlich nur den Zugriff auf die interne Funktionalität dar. In Deinem Fall würde hierüber der Zugriff auf die blockende canRead Funktion bereitgestellt werden. Die Asynchronität wird im Service implemntiert. Das sich dies "selbst" aktiviert ist aber eigentlich nicht Sinn der Sache. Nachdem eine asynchrone Operation abgelaufen ist, wird der übergebene Handler tätig, und der Aufrufer muss die nächste asynchrone Operation starten.

    Werner Salomon schrieb:

    [...]- wenn ein Paket am Bus empfangen wird, muss ich es abhängig vom Inhalt dispatchen. Wer ist dafür zuständig? Ich könnte mir auch eine Lösung mit mehr als einem Handler vorstellen. Macht das Sinn?[...]

    Was soll denn im Handler des Aufrufers bereitgestellt werden? Vielleicht macht es Sinn, mehrere Handler bereitzustellen, und dann, abhängig vom Dispatchergebnis, den richtigen aufzurufen.



  • Hallo Tachyon,

    Danke für Deine Antwort.
    Das hatte ich schon irgendwie geahnt, dass der, der das erste async_read macht, auch das folgende async_read machen sollte. Also kein 'selber zu posten, damit es weiter geht'.

    Was ich hier vorliegen habe sind ca. 80 Objekte aus ca. 30 unterschiedlichen Klassen, die Pakete vom CAN-Bus entgegennehmen sollen. Die Frequenz liegt im Schnitt bei ca. 100Paketen pro Sekunde, das kann aber kurzfristig (für eine 1/10s oder so) auch ein Mehrfaches davon annehmen und die Pakete sollen auch dann genauso schnell bearbeitet bzw. beantwortet werden. Ein Paket ist recht klein: 8Byte plus ein long plus ein wenig Verwaltungsinformation.

    Aus Gründen, die von der Treiber-API herrühren, werde ich nur eine IO-Objekt für alle Empfängerobjekte implementieren können (nur ein Handle). Das bedeutet, dass die 'WaitingOperation' nach dem read das Paket möglichst sofort loswerden muss (noch 'n Post) oder dass ich für den asio::io_service mehrere Threads spendieren, die ich aber wiederum serialisieren muss, da zu einer Zeit immer nur genau ein read (für das Handle) anstehen darf. Es wäre definitiv zu langsam, wenn nach Eintreffen eines Pakets dieses erst bearbeitet wird und anschließend ein async_read gemacht wird. Derweil könnten 79 andere Objekte auch ein Paket empfangen.

    Der Empfänger wird erst aus dem Inhalt des Pakets bestimmt, daher macht es in meinen Augen keinen Sinn, dass die Empfänger-Objekte das async_read aufsetzen, da der übergebene Handler (oder das, was er aufruft) wohl auch das Dispatching machen muss. D.h. für den (asnc_)read, den ein Empfänger aufrufen würde, bekommt mit hoher Wahrscheinlich ein anderer Empfänger das gelesene Paket.

    Ich hätte schon eine Idee wie man das implementiert, aber noch mal die Frage: Hat irgendwer eine Vorstellung, wie da der Königsweg ist?
    Gibt es ein paar boost.asio-spezifische Regeln, die man einhalten sollte?

    Gruß
    Werner



  • Sorry, das ich erst jetzt antworte. Wollte das eigentlich schon viel früher machen.

    Ich würde hierfür einen Service aufsetzen, der Rohdaten empfängt, ( async_read gibt einen Buffer aus Bytes zurück) und das Dispatching hinterher machen. Das hat eigentlich nichts direkt im Empfänger zu suchen. Wenn Du das Dispatching serialisieren willst, könntest Du einfach Dispatch-Handler in einen io_service posten, für den Du die run-Methode in einen Threadpool mit der gewünschten Anzahl an Threads laufen lässt.


Anmelden zum Antworten