Forum: PC-Programmierung C++ Boost.Asio(TCP/IP) Restfragen


von AsioPP (Gast)


Lesenswert?

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

von Nick M. (Gast)


Lesenswert?

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.

von mh (Gast)


Lesenswert?

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?

von Nick M. (Gast)


Lesenswert?

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?

von cppbert3 (Gast)


Lesenswert?

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

von mh (Gast)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

mh schrieb:
> Ich habe nichts von OOP geschrieben.

Und warum hast du mich dann auf C++ hingewiesen? Dachtest du ich bin von 
VB ausgegangen?

von mh (Gast)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

mh schrieb:
> nur eben kein handle, weil keins gebraucht wird.

Ach, dein handle nennt sich *this. Darum "brauchst du keines".

von mh (Gast)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

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.

von mh (Gast)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

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.

von AsioPP (Gast)


Lesenswert?

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.

von AsioPP (Gast)


Lesenswert?

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?

von mh (Gast)


Lesenswert?

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.

von cppbert3 (Gast)


Lesenswert?

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

von AsioPP (Gast)


Lesenswert?

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?

von AsioPP (Gast)


Lesenswert?

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?

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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 :)

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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"

von cppbert3 (Gast)


Lesenswert?

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

von AsioPP (Gast)


Lesenswert?

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?

von mh (Gast)


Lesenswert?

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.)

von Nick M. (Gast)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

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.

von AsioPP (Gast)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

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.

von mh (Gast)


Lesenswert?

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.

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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

von Nick M. (Gast)


Lesenswert?

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!

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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

von mh (Gast)


Lesenswert?

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.

von cppbert3 (Gast)


Lesenswert?

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

von Nick M. (Gast)


Lesenswert?

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.

von AsioPP (Gast)


Lesenswert?

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?

von cppbert3 (Gast)


Lesenswert?

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

von mh (Gast)


Lesenswert?

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.

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

> 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

von cppbert3 (Gast)


Lesenswert?

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

von AsioPP (Gast)


Lesenswert?

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.

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

Nicht dein while(1) treibt dein Programm sonder die Verknüpfung von 
ende-"callbacks"

von AsioPP (Gast)


Lesenswert?

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 ;-)

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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

von mh (Gast)


Lesenswert?

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.

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

Und natürlich darfst du 7n dem handler auf deine eigenen Daten z.b. in 
der queue warten, völlig ok

von cppbert3 (Gast)


Lesenswert?

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)

von AsioPP (Gast)


Lesenswert?

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?

von AsioPP (Gast)


Lesenswert?

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
}

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

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
Noch kein Account? Hier anmelden.