Hallo, ich beschäftige mich mit boost.asio für TCP/IP und habe jetzt einen "gewissen Überblick" würde aber gerne nochmal auf ein paar Dinge eingehen: Frage 1: asynchron vs synchron: Wenn ich es richtig verstehe läuft es so: async_write: blockiert nicht, code wird anschließend sofort fortgeführt. write: blockiert solange bis Daten in die Netzwerkkarte/OS geschrieben sind. https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/async_write/overload1.html https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/write/overload1.html Zu async allerdings das: ZITAT: "data muss jedoch solange existieren, bis die asynchrone Operation beendet ist und die Variable nicht mehr benötigt wird. Indem data als globale Variable definiert ist, ist sichergestellt, dass sich der Gültigkeitsbereich der Variablen über die gesamte asynchrone Operation erstreckt." https://dieboostcppbibliotheken.de/boost.asio-netzwerkprogrammierung Ich frage mich daher was das asynchrone senden von Daten für einen realen Vorteil hat, wenn doch die Quellvariablen solange existieren müssen, bis die asynchrone Operation beendet ist. Natürlich ist das reine async_write zunächst schneller weil es nicht blockiert aber dafür zu sorgen, dass die Quellvariablen im Scope vorhanden bleiben müssen, hebelt die Vorteile von der asynchronen Operation doch völlig aus!? Ich sehe da eigentlich das synchrone write im Vorteil denn direkt danach darf die Quelle verschwinden/überschrieben werden. Das bietet meiner Meinung nach auch weniger Fehlerpotential. Liege ich da richtig oder hab ich was übersehen? Die folgenden Fragen 2+3 sind natürlich ohne konkrete Messungen nicht präzise zu beantworten und je nach Infrastruktur auch etwas anders, es geht mir eher eine grobe Vorstellung zu bekommen, einen Überblick wie sich das etwa verhält. Ganz grobe Einschätzungen/Tendenz wäre schon super interessant für mich. Frage 2: Geschwindigkeit: Wie schnell ist das synchrone write ungefähr abgearbeitet, sind das ganz grob eher ns oder ms (Wenn man Beispielsweise 80 Byte sendet)? Fällt eher der Basis-overhead für das write ins Gewicht oder hängt das komplett nur von der Datenmenge ab die gesendet wird? Ich vestehe es so, dass write die Daten in die Netzwerkkarte/OS schickt und wenn diese alles aufgenommen hat gehts weiter. Wenns erstmal ins OS geht und dann in die Netzwerkkarte dann ist ein synchrone write sicherlich auch recht flott oder. Frage 3: Datenmenge: Gibt es eine ungefähre Datenmenge ab der sich das synchrone write merklich verlangsamt weil Netzwerkkarte/OS nicht mehr aufnehmen/verarbeiten kann? Also sind zB 80 Byte und 2 MB fast gleich schnell aber dann 50MB besonders langsam? Gibt es sowas wie eine Grafik wo man Datengröße und dessen Dauer eines synchronen writes im Zusammenhang sieht? Ist das mit steigender Datenmenge eher eine lineare Kurve oder exponentiell oder gibts irgendwo einen Sprung (weil zB Netzwerkkarte bestimmten Speicher hat)? Frage 4: Bleibt ein gesendeter Daten-Block immer zusammenhängend?: Vorausgesetzt es läuft im Netzwerk alles normal und Fehlerfrei, kann man sich darauf verlassen, dass der Empfänger immer das komplette Stück Daten in der gleichen Byte-Reihenfolge bekommt wie es durch write rausging? Beispielsweise wenn jetzt in verschiedenen Threads mehrere writes parallel daten senden, bekommt der Empfänger per read dann immer die vollständigen Daten-Blöcke ohne das sich die Daten der verschiedenen writes "vermischen"? Viele Grüße
AsioPP schrieb: > Natürlich ist das > reine async_write zunächst schneller weil es nicht blockiert aber dafür > zu sorgen, dass die Quellvariablen im Scope vorhanden bleiben müssen, > hebelt die Vorteile von der asynchronen Operation doch völlig aus!? Nennt sich "Design-Fehler". Das async write müsste die Daten kopieren (kostet Zeit) oder eine callback-function (mit handle) aufrufen die dann den Speicher evtl. deallokiert. Die Lösung mit call back wäre besser, denn das async-write kann nicht vorher sicherstellen, dass genug freier Puffer im TCP/IP stack frei ist.
Nick M. schrieb: > AsioPP schrieb: >> Natürlich ist das >> reine async_write zunächst schneller weil es nicht blockiert aber dafür >> zu sorgen, dass die Quellvariablen im Scope vorhanden bleiben müssen, >> hebelt die Vorteile von der asynchronen Operation doch völlig aus!? > > Nennt sich "Design-Fehler". Das async write müsste die Daten kopieren > (kostet Zeit) oder eine callback-function (mit handle) aufrufen die dann > den Speicher evtl. deallokiert. > Die Lösung mit call back wäre besser, denn das async-write kann nicht > vorher sicherstellen, dass genug freier Puffer im TCP/IP stack frei ist. Dir ist klar, dass es sich um C++ handelt? Warum sollte man da ein handle benutzen? Was willst du noch als weitere callback-function neben handler?
mh schrieb: > Was willst du noch als weitere callback-function neben handler? Dir ist klar, dass ein handler was Anderes ist wie ein handle? Und was hat OOP damit zu tun? Willst du an async-write ein Objekt übergeben?
AsioPP schrieb: > Ich frage mich daher was das asynchrone senden von Daten für einen > realen Vorteil hat, wenn doch die Quellvariablen solange existieren > müssen, bis die asynchrone Operation beendet ist. Der Vorteil ist nicht unbedingt die leichtere Programmierbarkeit, du kannst nur ohne deine CPU zu viel in Wartezustände zu halten deine Netzwerkkarte unter Last halten und hast dein viel Flexibilität, Performant heisst in den meisten Fällen nicht on-the-fly Allokation oder Datenkopien AsioPP schrieb: > Vorausgesetzt es läuft im Netzwerk alles normal und Fehlerfrei, kann man > sich darauf verlassen, dass der Empfänger immer das komplette Stück > Daten in der gleichen Byte-Reihenfolge bekommt wie es durch write > rausging? Ja dafür sorgt der TCP Stack, aber keine Magie sorgt dafür von dir sinnlos zerhackte Paketen irgendwie wieder kombinierte werden
Nick M. schrieb: > mh schrieb: >> Was willst du noch als weitere callback-function neben handler? > > Dir ist klar, dass ein handler was Anderes ist wie ein handle? > Und was hat OOP damit zu tun? Willst du an async-write ein Objekt > übergeben? Ich habe nichts von OOP geschrieben. Man kann Objekte benutzen, ohne OOP anzuwenden. Und warum sollte man kein Objekt übergeben? Es gibt Requierments an den template Parameter AsyncWriteStream, wenn ein Objekt die erfüllt, kann man es an dieser Stelle nutzen, so wie das alle Beispiele und Tutorials auf der Boost::asio Seite tun.
mh schrieb: > Ich habe nichts von OOP geschrieben. Und warum hast du mich dann auf C++ hingewiesen? Dachtest du ich bin von VB ausgegangen?
Nick M. schrieb: > mh schrieb: >> Ich habe nichts von OOP geschrieben. > > Und warum hast du mich dann auf C++ hingewiesen? Dachtest du ich bin von > VB ausgegangen? Nein, aber deine Post mit Nick M. schrieb: > eine callback-function (mit handle) klingt sehr nach C. Und async_write hat einen callback, nur eben kein handle, weil keins gebraucht wird.
mh schrieb: > nur eben kein handle, weil keins gebraucht wird. Ach, dein handle nennt sich *this. Darum "brauchst du keines".
Nick M. schrieb: > mh schrieb: >> nur eben kein handle, weil keins gebraucht wird. > > Ach, dein handle nennt sich *this. Darum "brauchst du keines". Am Ende läuft es auf einen this-Pointer hinaus, man benutzt eben Objekte. Aber ein Lambda das man als handler übergibt hat wenig mit einem "handle" zu tun. Aber wenn du umbedingt willst, dann hat async-write eine callback-funktion mit handle, es gibt also keinen Grund von Design-Fehler zu sprechen.
mh schrieb: > dann hat > async-write eine callback-funktion mit handle, es gibt also keinen Grund > von Design-Fehler zu sprechen. Dann ist es tatsächlich kein Designfehler. D'accord. Dann ist aber das (aus dem Ursprungsposting): "Indem data als globale Variable definiert ist, ist sichergestellt, dass sich der Gültigkeitsbereich der Variablen über die gesamte asynchrone Operation erstreckt." durchaus irreführend und wurde vom Autor (nicht der TO) falsch verstanden. Und dann vom TO falsch übernommen.
Nick M. schrieb: > mh schrieb: >> dann hat >> async-write eine callback-funktion mit handle, es gibt also keinen Grund >> von Design-Fehler zu sprechen. > > Dann ist es tatsächlich kein Designfehler. D'accord. > > Dann ist aber das (aus dem Ursprungsposting): > [...] Dann verstehe ich ernsthaft nicht, warum du antwortest, oder nicht wenigstens anmerkst, dass du dich nicht mit asio auskennst. Der TO hat sogar einen Link auf die Beschreibung von async_write gepostet, in dem alles nötige beschrieben wird.
mh schrieb: > Der TO hat sogar einen Link auf die Beschreibung von async_write > gepostet, in dem alles nötige beschrieben wird. Ich zitiere nochmal: AsioPP schrieb: > ZITAT: "data muss jedoch solange existieren, bis die asynchrone > Operation beendet ist und die Variable nicht mehr benötigt wird. Indem > data als globale Variable definiert ist, ist sichergestellt, dass sich > der Gültigkeitsbereich der Variablen über die gesamte asynchrone > Operation erstreckt." Da gibts doch wenig zu diskutieren drüber das eine globale Variable ist und warum das so reichlich unsinnig oder zumindest bewusst schlampig ist.
cppbert3 schrieb: > Der Vorteil ist nicht unbedingt die leichtere Programmierbarkeit, du > kannst nur ohne deine CPU zu viel in Wartezustände zu halten deine > Netzwerkkarte unter Last halten und hast dein viel Flexibilität, > Performant heisst in den meisten Fällen nicht on-the-fly Allokation oder > Datenkopien Alles klar. Mal angenommen man will die Sache asyncron machen und hat den Fall, dass sich die zu sendenden Daten in einem Scope befinden der nach dem async_write schnell verlassen wird. Was wäre denn die normale Lösung um Daten für async_write solange am Leben zu halten wie nötig? (Eine globale Variable wie in dem Beispiel was ich verlinkt habe wäre wohl unsinnig (und sowieso unschick) wenn es um viele hintereinander auszuführende async_write geht). Soweit nochmal zu Frage 1. cppbert3 schrieb: > Ja dafür sorgt der TCP Stack, aber keine Magie sorgt dafür von dir > sinnlos zerhackte Paketen irgendwie wieder kombinierte werden Sehr gut, danke damit wäre Frage 4 auf jeden Fall schon mal geklärt. Ja stimmt zerhacken ist nicht so gut, macht vielleicht erst ab einer gewissen sehr großen Datenmenge Sinn. Fällt jemandem noch etwas zu Frage 2+3 ein, ganz grob? Diese Themen sollten in anderen Sprachen und anderen libs vermutlich ähnlich sein.
Nick M. schrieb: > das eine globale Variable ist > und warum das so reichlich unsinnig oder zumindest bewusst schlampig > ist. Wegen diesem Beispiel mit der globalen Variable kam mir auch das ganze async_write ja auch direkt blöd vor. Aber genau das interessiert mich noch, was ist denn die bessere Variante? also die Lösung um Daten für async_write solange am Leben zu halten wie nötig?
Nick M. schrieb: > mh schrieb: >> Der TO hat sogar einen Link auf die Beschreibung von async_write >> gepostet, in dem alles nötige beschrieben wird. > > Ich zitiere nochmal: > > AsioPP schrieb: > [...] > > Da gibts doch wenig zu diskutieren drüber das eine globale Variable ist > und warum das so reichlich unsinnig oder zumindest bewusst schlampig > ist. Darüber nicht, das ist offensichtlich falsch. Aber das kommt auch nicht von der offiziellen Seite. Kurz vor dem von dir zitierten Text, stehen diese beiden Links auf die echte boost::asio Doku. AsioPP schrieb: > https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/async_write/overload1.html > https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/write/overload1.html Der Aufwand war also minimal, auch wenn man boost::asio noch nicht selbst benutzt hat. AsioPP schrieb: > Fällt jemandem noch etwas zu Frage 2+3 ein, ganz grob? Diese Themen > sollten in anderen Sprachen und anderen libs vermutlich ähnlich sein. Das sind Fragen, die man so nicht beantworten kann. Das hängt ganz stark davon ab, was du für ein System (Hardware, OS, ...) hast und was sonst noch auf diesem System läuft.
Nick M. schrieb: > Da gibts doch wenig zu diskutieren drüber das eine globale Variable ist > und warum das so reichlich unsinnig oder zumindest bewusst schlampig > ist. Wie kommst du auf globale? Ich denke auch das du keine Ahnung von Asio hast und nur am spekulieren bist, das hilft nicht wirklich
Ok man sollte wohl bei async_write einen boost::asio::buffer nutzen wie es aussieht. Also verhält sich wohl ein bisschen so wie ein smart-pointer. boost::asio::async_write(s, boost::asio::buffer(data, size), handler); Ist folgende Aussage korrekt?: mit obiger Zeile werden die Daten (data, size) im RAM (boost::asio::buffer) für exakt dieses async_write zwischengespeichert und bleibt auch nach verlassen des Scopes im RAM erhalten? Und wird nach erfolgter write operation gelöscht? Ist das so?
Und noch eine Frage zu: "The program must ensure that the stream performs no other write operations (such as async_write, the stream's async_write_some function, or any other composed operations that perform writes) until this operation completes." https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/async_write/overload1.html Heißt das ich darf während einer async_write operation parallel keine weitere starten?
AsioPP schrieb: > Nick M. schrieb: > das eine globale Variable ist > und warum das so reichlich unsinnig oder zumindest bewusst schlampig > ist. > > Wegen diesem Beispiel mit der globalen Variable kam mir auch das ganze > async_write ja auch direkt blöd vor. Aber genau das interessiert mich > noch, was ist denn die bessere Variante? also die Lösung um Daten für > async_write solange am Leben zu halten wie nötig? Stell dir vor du hast einen Ringpuffer oder Queue die beim receive weiterschaltet auf den nächsten Send buffer usw. Viele Leute die das Async Pattern nicht (richtig) verstanden haben denken immer das wäre eine coolere Variante von Blocking, Effizients bedeutet im Normalfall nicht das dadurch dein Code einfacher oder weniger Fehleranfällig wird, das muss man erstmal akzeptieren, viele haben aber auch keine solche Anforderungen, ich hab z.B. mit Asio eine 1024 Geräte (Sever) Simulation mit ordentlich Datendurchsatz realisiert welche die CPU kaum stresst und weil ich so detailierte Kontrolle über meine Puffer habe auch ein sehr schnelles Speichermanagment für meine Server mit geringer Latenz So was wird sehr schwer wenn die Konzepte zuu highlevel sind, wobei Asio mehrere Abstraktionsschichten hat wo man immer mehr Kontrolle übernehmen kann, nicht ohne Grund wird Asio und seine Executor einen grossteil des C++23 Networkstacks bilden
AsioPP schrieb: > Ok man sollte wohl bei async_write einen boost::asio::buffer > nutzen wie es aussieht. Also verhält sich wohl ein bisschen so wie ein > smart-pointer. > > boost::asio::async_write(s, boost::asio::buffer(data, size), handler); > Ist folgende Aussage korrekt?: mit obiger Zeile werden die Daten (data, > size) im RAM (boost::asio::buffer) für exakt dieses async_write > zwischengespeichert und bleibt auch nach verlassen des Scopes im RAM > erhalten? Und wird nach erfolgter write operation gelöscht? > Ist das so? Einfach ein bisschen die Beispiele anschauen, Asio KANN das für dich übernehmen oder es ist nur ein Slice auf dein Managment, viel Kombinationsmöglichkeit hier um dich nicht auf ein Konzept einzuschränken was natürlich das lernen erschwert :)
AsioPP schrieb: > Und noch eine Frage zu: > "The program must ensure that the stream performs no other write > operations (such as async_write, the stream's async_write_some function, > or any other composed operations that perform writes) until this > operation completes." > https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/async_write/overload1.html > > Heißt das ich darf während einer async_write operation parallel keine > weitere starten? Multithreading mit Blocking oder Asynchron mit n Producern nicht noch Multithreading in die Asynchronität rein bringen, ist doppeltdreifach gemoppelt, sonst brauchts ja wieder überall Locks um das abzusichern, was wieder ineffizient ist Async ist ein komplettes Pattern nicht einfach nur eine andere Art von Write Das ist kein einfaches async_write mit waitstate,event
Das boost asio async (Reactor,Proactor?) Pattern erlaubt Asynchronität ohne Threads, da Threads ein zu bauen ist so sinnvoll wie gleichzeitig vorwärts und rückwärts fahren
AsioPP schrieb: > Heißt das ich darf während einer async_write operation parallel keine > weitere starten? Auf der gleichen Verbindung ist das doch völlig sinnfrei und warum sollte das für andere Verbindungen notwendig sein, "Use the Force äh your Brain, AsioPP"
cppbert3 schrieb: > Auf der gleichen Verbindung ist das doch völlig sinnfrei Falls bei dir nichts Threads irgendeine Flusskontrollaufgabe machen, was bei Asio aber einfach nicht nötig, eher sogar hinderlich unsinnig ist
cppbert3 schrieb: > Auf der gleichen Verbindung ist das doch völlig sinnfrei und warum > sollte das für andere Verbindungen notwendig sein Ich meinte eher ob man ein async_write ausführen kann wenn ein anderes zuvor noch nicht fertig ist. Also zB ganz fiktiv eine while(1) dort entstehen Daten die bei jedem Durchlauf geschickt werden sollen. Wenn ein neuer Schleifendurchlauf ein neues async_write aufruft wäre das ein Problem wenn das vorige async_write noch nicht fertig ist im Hintergrund?
AsioPP schrieb: > Wenn > ein neuer Schleifendurchlauf ein neues async_write aufruft wäre das ein > Problem wenn das vorige async_write noch nicht fertig ist im > Hintergrund? Vermutlich, die Doku sagt, dass du das nicht machen darfst. Du musst an der Stelle warten, was nicht so wirklich schön ist, oder es über einen geeigneten handler regeln. Du solltest dir dringend die ganze Doku durchlesen und dir die Beispiele angucken. Wenn du verstanden hast, wie die Beispiele funktionieren, kannst du nochmal über deine eigene Anwendung nachdenken. (Um die c++17 Beispiele solltest du vermutlich einen Bogen machen, da coroutinen wahrscheinlich wenig zu deinem asio Verständnis beitragen werden.)
AsioPP schrieb: > Wegen diesem Beispiel mit der globalen Variable kam mir auch das ganze > async_write ja auch direkt blöd vor. Das ist völlig i.O. das für blöd zu halten. Der Fehler liegt in deiner Quelle für das Zitat, nicht bei dir. Die Lösung ist, den Handler vom Async_write zu verwenden der dann ein free auf die Daten macht. Du musst also einen allokierten Speicher übergeben. Das ist der Preis dafür dann gleich mit was Anderem weitermachen zu können. Meiner Meinung nach ist es dass immer wert (solange man irgendwann auch überprüfen kann, ob die Daten auch geschickt wurden). Oder man überprüft vorm Versenden, ob der Puffer für den socket nicht am Überlaufen ist.
mh schrieb: > Der Aufwand war also minimal, auch wenn man boost::asio noch nicht > selbst benutzt hat. Und dennoch hat der TO das zitiert auf das ich mich bezogen hab. Ihm hat die Lösung zu Recht jedenfalls nicht gefallen. Dass er das mit dem Handler nicht gelesen hat, ist nicht mein Problem.
Ok vielen Dank schon mal an alle für den Input das war schon mal sehr interessant alles, was Frage 1 angeht da tendiere ich gerade stark zum synchronen write da mir das asynchrone doch zu viele Stoplperfallen hat, was natürlich auch an meinen noch beschränkten Kenntnissen liegt. Sollte write "zu langsam" sein, was ich eigentlich nicht unbedingt erwarte, kann man dann immer noch mal tiefer ins asynchrone einsteigen. Was die Fragen 2+3 angeht muss ich dann einfach selber mal messen und rumprobieren.
AsioPP schrieb: > Wenn > ein neuer Schleifendurchlauf ein neues async_write aufruft wäre das ein > Problem wenn das vorige async_write noch nicht fertig ist im > Hintergrund? Mal ernsthaft: Wenn sich das async_write nennt, non-blocking ist, dann muss es auch asynchron arbeiten können. Und halt dann eine queue bereithalten für die noch wartenden writes. Andernfalls wäre es ein async-write das nach der ersten Verwendung ein blocking sync_write ist und dann absolut lächerlich.
AsioPP schrieb: > Frage 1 angeht da tendiere ich gerade stark zum > synchronen write da mir das asynchrone doch zu viele Stoplperfallen hat, > was natürlich auch an meinen noch beschränkten Kenntnissen liegt. > Sollte write "zu langsam" sein, was ich eigentlich nicht unbedingt > erwarte, kann man dann immer noch mal tiefer ins asynchrone einsteigen. > Was die Fragen 2+3 angeht muss ich dann einfach selber mal messen und > rumprobieren. Das ist genau das richtige Vorgehen, nimm synchron, wenn du damit besser klar kommst, bis du einen Grund hast asynchron zu nutzen. Nick M. schrieb: > mh schrieb: >> Der Aufwand war also minimal, auch wenn man boost::asio noch nicht >> selbst benutzt hat. > > Und dennoch hat der TO das zitiert auf das ich mich bezogen hab. > Ihm hat die Lösung zu Recht jedenfalls nicht gefallen. Dass er das mit > dem Handler nicht gelesen hat, ist nicht mein Problem. Natürlich ist es nicht dein Problem, wenn du auf Fragen antwortest, ohne Ahnung vom Thema zu haben und ohne auf diesen Umstand hinzuweisen. Das ist das Problem des Fragestellers, der mit deiner unsinnigen Antwort leben muss. Nick M. schrieb: > AsioPP schrieb: >> Wenn >> ein neuer Schleifendurchlauf ein neues async_write aufruft wäre das ein >> Problem wenn das vorige async_write noch nicht fertig ist im >> Hintergrund? > > Mal ernsthaft: > Wenn sich das async_write nennt, non-blocking ist, dann muss es auch > asynchron arbeiten können. Und halt dann eine queue bereithalten für die > noch wartenden writes. > > Andernfalls wäre es ein async-write das nach der ersten Verwendung ein > blocking sync_write ist und dann absolut lächerlich. Mal ernsthaft: Du hast ausgiebig gezeigt, dass du von diesem Thema keine Ahnung hast. Es ist lächerlich, dass du ein Urteil über boost::asio fällst, deren Entwickler offensichtlich deutlich mehr Ahnung haben als du.
AsioPP schrieb: > cppbert3 schrieb: > Auf der gleichen Verbindung ist das doch völlig sinnfrei und warum > sollte das für andere Verbindungen notwendig sein > > Ich meinte eher ob man ein async_write ausführen kann wenn ein anderes > zuvor noch nicht fertig ist. Also zB ganz fiktiv eine while(1) dort > entstehen Daten die bei jedem Durchlauf geschickt werden sollen. Wenn > ein neuer Schleifendurchlauf ein neues async_write aufruft wäre das ein > Problem wenn das vorige async_write noch nicht fertig ist im > Hintergrund? Für die selbe oder unterschiedliche Verbindungen? Dein write_async stösst in seinem Feedback das nächste write in deiner Queue oder wie auch immer sich deine Befehle ergeben an, sonst nutzt du nicht das async Pattern sondern erwartest das du nur eine einfache asynchrone Api hast, was Asio nicht ist
AsioPP schrieb: > Ok vielen Dank schon mal an alle für den Input das war schon mal > sehr interessant alles, was Frage 1 angeht da tendiere ich gerade stark > zum synchronen write da mir das asynchrone doch zu viele Stoplperfallen > hat, Wollte ich dir auch gerade zu raten, wenn man das async Pattern nicht richtig versteht kommt da komischer Code raus, bei synchron bist du schon sicher also nimm erst mal das
mh schrieb: > Es ist lächerlich, dass du ein Urteil über boost::asio fällst, deren > Entwickler offensichtlich deutlich mehr Ahnung haben als du. Du meinst also dass dieses kaputte Verhalten von async_write so wie du es beschreibst Ausdruck von viel Ahnung ist? Ich lass das gerne so stehen!
Wenn du dich z.b. nur im 5-10 Verbindungrahmen bewegst merkst du keinen Unterschied, besonders dann wenn dein gewohntes Asynchronprinzip Threads sind Wenn du hunderte oder tausende Verbindungen hast merkt man schnell das es dann sehr ineffizient wird, bei dem Asyc Pattern von Asio würde das nicht sonderlich ins Gewicht fallen, dafür muss man sich aber auf das Pattern einlassen sonst bringt es nichts und auch erst dann erkennst du den Nutzen deutlich
Nick M. schrieb: > mh schrieb: > Es ist lächerlich, dass du ein Urteil über boost::asio fällst, deren > Entwickler offensichtlich deutlich mehr Ahnung haben als du. > > Du meinst also dass dieses kaputte Verhalten von async_write so wie du > es beschreibst Ausdruck von viel Ahnung ist? > > Ich lass das gerne so stehen! Ja, sehr viel Ahnung - schon mal einen Netzwerkserver geschrieben der mehrere zehntausen Verbindungen managed? Wenn du das brauchst kommst nicht um das Pattern rum - du siehst die effiziens und eleganz nur nicht weil du damit noch nicht intensiv gearbeitet hast oder solche Anforderungen noch nicht auf deinem Tisch gelegen haben
Nick M. schrieb: > mh schrieb: >> Es ist lächerlich, dass du ein Urteil über boost::asio fällst, deren >> Entwickler offensichtlich deutlich mehr Ahnung haben als du. > > Du meinst also dass dieses kaputte Verhalten von async_write so wie du > es beschreibst Ausdruck von viel Ahnung ist? > > Ich lass das gerne so stehen! Nur weil du behauptes, dass es kaputt ist, ist es das noch lange nicht. Mir ist diese asynchrone Verhalten schon häufiger über den Weg gelaufen und ich habe bis heute noch Niemanden getroffen, der das Verhalten verstenden hat und sich darüber beschwert.
mh schrieb: > Nick M. schrieb: > mh schrieb: > Es ist lächerlich, dass du ein Urteil über boost::asio fällst, deren > Entwickler offensichtlich deutlich mehr Ahnung haben als du. > > Du meinst also dass dieses kaputte Verhalten von async_write so wie du > es beschreibst Ausdruck von viel Ahnung ist? > Ich lass das gerne so stehen! > > Nur weil du behauptes, dass es kaputt ist, ist es das noch lange nicht. > Mir ist diese asynchrone Verhalten schon häufiger über den Weg gelaufen > und ich habe bis heute noch Niemanden getroffen, der das Verhalten > verstenden hat und sich darüber beschwert. Er hatte die Anforderungen vielleicht noch nicht und vielleicht keiner Erfahrung mit der Qualität der Boost oder Boost-nahen Libs, da sei auch unbegründete Kritik erlaubt, hab auch schon genug Neztwerlibs gesehen die Schrott waren
mh schrieb: > Nur weil du behauptes, dass es kaputt ist, ist es das noch lange nicht. Dann ist der Name kaputt. Asynchron ist das jedenfalls nicht. Ich hab genau sowas auf einem µC impelemntiert der über TCP/IP Daten von verschiedenen Quellen über einen Socket schickt. Jede Quelle schickt ihr Datenpacket an einen Task. Und der baut sich eine Queue auf in der die Packete chronologisch stehen und auch so abgearbeitet werden. Die Absender können derweilen einfach weiterlaufen und auch gerne noch ein Paket anhängen. Und wenn die Queue überläuft (weil Server weg) wird derweilen über SD-Card weiter gequeued. Bis die überläuft (nach 4 GB). Genauso läuft es mit der SD-Card, da können beliebig viele Daten geschickt werden. Die Absender werden nicht blockiert, die SD-card arbeitet sequentiell alles ab und schickt sich sogar eigene Kommandos. Lese-Anfragen werden dann asynchron beantwortet wenn sie in der Queue an der Reihe sind. Also ich weiß schon wovon ich rede und kann gute, schlechte und kaputte Implementierungen sehr wohl erkennen.
cppbert3 schrieb: > Für die selbe oder unterschiedliche Verbindungen? ja genau die selbe Verbindung. Mir gings nur um die Aussage: "The program must ensure that the stream performs no other write operations (such as async_write, the stream's async_write_some function, or any other composed operations that perform writes) until this operation completes." https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/async_write/overload1.html Das bedeutet doch (basierend auf einer simplen Verbindung), dass async_write für den Anwendungsfall wo async_write oft sehr schnell hintereinander aufgerufen wird nicht so gut geeignet ist, man könnte vielleicht sagen, async_write ist perfekt wenn danach anderer code kommt der sofort ausgeführt werden soll aber nicht unbedingt wenn danach sehr schnell ein weiteres async_write kommt richtig?
Nick M. schrieb: >> mh schrieb: >> Nur weil du behauptes, dass es kaputt ist, ist es das noch lange nicht. > Also ich weiß schon wovon ich rede und kann gute, schlechte und kaputte > Implementierungen sehr wohl erkennen. Es stellt kein in Frage das du schon was mit asynchronität und Netzwerk gemacht hast, ich stelle in Frage das du weisst wie eine generische Api aussehen muss die gut genug ist den nächsten C++ Standard zu definieren und die u.a. das 1Mio Connection Problem lösen kann, was Asio schafft Bei Asio sind die Namen genau so klar und verständlich wie in jeder Async Pattern Api Implementation, es ist eben keine einfache Asyc Api sondern das Pattern um u.a. das 1Mio Connections Problem abzudecken, das ist etwas völlig anderes, weil die Skalierung in den Dimensionen wirklich ein Problem ist
cppbert3 schrieb: > Er hatte die Anforderungen vielleicht noch nicht und vielleicht keiner > Erfahrung mit der Qualität der Boost oder Boost-nahen Libs, da sei auch > unbegründete Kritik erlaubt, hab auch schon genug Neztwerlibs gesehen > die Schrott waren Keine Ahnung haben ist also eine ausreichende Begründung, um Unsinn von sich zu geben? Das ist vielleicht als erste Reaktion so gerade Ok, aber irgendwann sollte man feststellen, dass man keine Ahnung hat und die Klappe halten. Nick M. schrieb: > mh schrieb: >> Nur weil du behauptes, dass es kaputt ist, ist es das noch lange nicht. > > Dann ist der Name kaputt. Asynchron ist das jedenfalls nicht. Natürlich ist das asynchron. Asynchron heißt nicht "ich kann machen was ich will" und es heißt auch nicht, dass man unter keinen Umständen warten muss. Nick M. schrieb: > Ich hab genau sowas auf einem µC impelemntiert der über TCP/IP Daten von > verschiedenen Quellen über einen Socket schickt. [...] Glückwunsch ... das heißt nicht, dass deine Vorstellung von asynchron die einzig Wahre ist.
AsioPP schrieb: > cppbert3 schrieb: > Für die selbe oder unterschiedliche Verbindungen? > > ja genau die selbe Verbindung. Mir gings nur um die Aussage: > "The program must ensure that the stream performs no other write > operations (such as async_write, the stream's async_write_some function, > or any other composed operations that perform writes) until this > operation completes." > https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/async_write/overload1.html > > Das bedeutet doch (basierend auf einer simplen Verbindung), dass > async_write für den Anwendungsfall wo async_write oft sehr schnell > hintereinander aufgerufen wird nicht so gut geeignet ist, man könnte > vielleicht sagen, async_write ist perfekt wenn danach anderer code kommt > der sofort ausgeführt werden soll aber nicht unbedingt wenn danach sehr > schnell ein weiteres async_write kommt richtig? Nicht wirklich Beispiel First packet Write current time ...löst on async write ende aus On asyc write ende Write current time ...und im Kreis Dann werden die Pakete so schnell rausgeschickt wie der Netzwerkstack leisten kann Dein sync write sieh eben so aus Loop: Write current time Wait write ende >loop
> Dein sync write sieh eben so aus > > Loop: > Write current time > Wait write ende > loop Und das wait write ende ist eben einfach zu implementieren aber ineffizent bei vielen Verbindungen, das ist der einzige Grund warum die asio Api nicht ganz trivial ist
mh schrieb: > Nick M. schrieb: >> Ich hab genau sowas auf einem µC impelemntiert der über TCP/IP Daten von >> verschiedenen Quellen über einen Socket schickt. [...] > Glückwunsch ... das heißt nicht, dass deine Vorstellung von asynchron > die einzig Wahre ist. Wenn er eine uC implementierung als Erfahrung ranzieht ist doch schon klar das die starken skalierungsrelevante Charakter der Asio API für ihn nicht so relevant ist
cppbert3 schrieb: > First packet > Write current time ...löst on async write ende aus > On asyc write ende > Write current time ...und im Kreis > > Dann werden die Pakete so schnell rausgeschickt wie der Netzwerkstack > leisten kann Jeder Aufruf von async_write müsste dann aber als Bedingung haben, dass der Handler eines vorigen async_write die operationen complete hat oder? Weil wenn man einfach schnelle viele async_write hintereinander ausführt würde man doch gegen die zitierte Regel verstoßen.
AsioPP schrieb: > cppbert3 schrieb: > First packet > Write current time ...löst on async write ende aus > On asyc write ende > Write current time ...und im Kreis > Dann werden die Pakete so schnell rausgeschickt wie der Netzwerkstack > leisten kann > > Jeder Aufruf von async_write müsste dann aber als Bedingung haben, dass > der Handler eines vorigen async_write die operationen complete hat oder? > Weil wenn man einfach schnelle viele async_write hintereinander ausführt > würde man doch gegen die zitierte Regel verstoßen. Das ist dein Verständisproblem: das write end ist kein Zustand sonder der anstosser für das nächste write oder die Datenerzeugung dafür, oder das aulesen aus einer Queue mit Userbefehlen die während dessen asynchron aufgelaufen sind
Nicht dein while(1) treibt dein Programm sonder die Verknüpfung von ende-"callbacks"
cppbert3 schrieb: > Das ist dein Verständisproblem: das write end ist kein Zustand sonder > der anstosser für das nächste write oder die Datenerzeugung dafür, oder > das aulesen aus einer Queue mit Userbefehlen die während dessen > asynchron aufgelaufen sind Ahh okay ich versuchs nochmal: Ist es also so dass async_write blockiert wenn ein voriges async_write noch nicht fertig ist? Und normal blockiert es nicht. Also bisschen wie ein Mutex. Falls das jetzt immer noch nicht richtig ist brächte ich eine Sendung mit der Maus Erklärung ;-)
https://www.boost.org/doc/libs/1_69_0/doc/html/boost_asio/example/cpp03/timeouts/async_tcp_client.cpp Schau dir start_write und handle_write an, handle_write ruft start_write auf, das ist keine rekursion
Lass dich von dem heartbeat timer und dem async wait nicht verwirren, das ist nur ein effizienter sleep, schlussendlich wird nach Zeit x wieder start_write aufgerufen
AsioPP schrieb: > cppbert3 schrieb: >> First packet >> Write current time ...löst on async write ende aus >> On asyc write ende >> Write current time ...und im Kreis >> >> Dann werden die Pakete so schnell rausgeschickt wie der Netzwerkstack >> leisten kann > > Jeder Aufruf von async_write müsste dann aber als Bedingung haben, dass > der Handler eines vorigen async_write die operationen complete hat oder? > Weil wenn man einfach schnelle viele async_write hintereinander ausführt > würde man doch gegen die zitierte Regel verstoßen. Ja, deswegen benutzt man den "handler". Ein minimales Beispiel:
1 | Queue queue; |
2 | |
3 | void handler() |
4 | {
|
5 | if(!queue.empty()) |
6 | {
|
7 | async_write(queue.pop(), handler); |
8 | }
|
9 | }
|
10 | |
11 | void main() |
12 | {
|
13 | queue = irgendwas(); |
14 | handler(); |
15 | }
|
Der handler wird automatisch aufgerufen, wenn async_write fertig ist. Genau dafür ist er da. Im handler kannst du jetzt gucken, ob es mehr Daten gibt und das nächste async_write aufrufen. Das ist ein bisschen so wie Rekursion (es ist aber keine!). Wie du im handler Zugriff auf diese Daten hast, ist deine Angelegenheit. Eine Möglichkeit ist sowas wie ne Queue.
Und ob die Queue global oder nicht ist liegt komplett in deinem ermessen, nichts erzwingt globale Variablen, nur der Entwickler selbst egal in welcher Situation
Und natürlich darfst du 7n dem handler auf deine eigenen Daten z.b. in der queue warten, völlig ok
Und der handler muss keine free-function sein, es geht auch ein Funktor oder Lambda, d.h. wie du den Zugriff auf deine Daten gestaltest ist sehr flexibel und dank Template-lastgkeit auch voll inline Fähig (falls möglich)
Ok verstehe immer mehr danke. mh schrieb: > void handler() > { > if(!queue.empty()) > { > async_write(queue.pop(), handler); > } > } > > void main() > { > queue = irgendwas(); > handler(); > } Ich überlege gerade, wenn man das in eine while(1) steckt würde man gegen die Regel verstoßen da handler() ständig aufgerufen wird: void main() { while(1) { queue = irgendwas(); handler(); } } In der While(1) würde man besser die queue nur mit den Daten des jeweiligen Durchlaufs füllen und async_write bewusst NICHT aufrufen weil man sonst gegen die Regel verstößt. handler() müsste aber doch immer getriggert werden wenn die queue nach dem leer sein einen neuen Dateneintrag bekommen hat was immer passiert wenn: - beim ersten Durchlauf - wenn queue leer gesendet wurde und dann wieder ein neuer Eintrag kommt. Solange Daten in der queue sind ruft sich handler() sozusagen selbst auf aber sobald alles gesendet wurde und die queue leer ist bräuchte man immer dann gezielt einen neuen Trigger wie macht man das?
Sozusagen wenn das die Basis wäre: std::queue<daten> queue_daten; while(1) { // hier entstehen Daten: daten quell_daten; // füge in queue hinzu: queue_daten.push(quell_daten); // ??? // handler(); würde gegen Regeln verstoßen }
AsioPP schrieb: > Sozusagen wenn das die Basis wäre: > > std::queue<daten> queue_daten; > > while(1) > { > // hier entstehen Daten: > daten quell_daten; > > // füge in queue hinzu: > queue_daten.push(quell_daten); > > // ??? > // handler(); würde gegen Regeln verstoßen > } Ganz klarer Regelverstoss, lasss das while weg und stosse die nächste Aktion über den Handler an, du versuchst das irgendwie in ein synchronablauf zu zwingen, was einfach völlig falsch ist Bei synchron ist eine Schleifer oder sowas dein Antrieb, bei asynchron mit Asio ist dein Handler derjenige der sagt was als nächstes kommt
AsioPP schrieb: > Solange Daten in der queue sind ruft sich handler() sozusagen selbst auf > aber sobald alles gesendet wurde und die queue leer ist bräuchte man > immer dann gezielt einen neuen Trigger wie macht man das? In dem du write aufrufst wenn neue Daten in die Queue gekommen sind da könntest du z.B. mit einer Condition drauf warten, oder mit if !queue.empty in deinem while poller (was aber einfach nur CPU Zeit frisst) Am bestes so was Starte write wenn neue Queuedaten da sind Write -> handle "loop" leert Queue so lange Daten da sind dann Ende, start erst wieder mit den nächste Queuedaten die rein kommen
AsioPP schrieb: > Sozusagen wenn das die Basis wäre: > > std::queue<daten> queue_daten; > > while(1) Nein, Basis ist in der Regel eine Instanz von `boost::asio::io_context`. Auf diesem Objekt rufst Du `.run()` auf. Das Objekt verwendest Du auch, um damit `acceptor` und `socket` zu konstruieren. All diese Objekte, die Du verwendest, um Verbindungen anzunehmen oder die Verbindungen selbst, nehmen eine Referenz auf dieses Objekt. Immer, wenn irgendwo ein Ereignis aufgetreten ist und der mit einer IO-Operation verknüpfte callback aufgerufen wird, wird dies aus dem Kontext von `boost::asio::io_context::run` passieren. Wenn Du irgendwo in dem asynchronen System einen blockenden Funktionsaufruf ausführst, oder selbst blockst, können keine callbacks mehr ausgeführt werden und das gesamte System steht. Boost.Asio hat eigentlich ein Vielzahl von sehr guten Beispielen. Boost.Asio ist aber auch nicht ganz trivial. Nimm Dir mal die Zeit und lese die Beispiele. Zu Deiner Ursprünglichen Frage: Wenn Du einen Puffer an async_write() weiter gibst, dann muss der Speicherbereich bis zur Ausführung des callbacks gültig bleiben. Die Asio Beispiele erreichen dies in der Regel dadurch, dass sie den Puffer in einem Kontext zur Verbindung (socket) speichern und einen shared_ptr<> auf diesen Kontext mit im callback speicher (boost::bind(), std::bind()). Wenn dann mehrere asynchrone Operationen für einen Kontext ausstehen (Lesen, Schreiben, Timeout) und jeder callback zu dieser Operation einen shared_ptr<> enthält, dann ist sichergestellt, dass exakt solange der Kontext erhalten bleibt.
:
Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.