Forum: PC-Programmierung Echtzeit für Socket Receive und TCP/IP Pakete


von Sascha S. (strongly-typed)


Lesenswert?

Hallo zusammen,

TLDR: In einer Applikation (x86 oder ARM Linux, nicht bare metal uC) 
muss ich innerhalb einer TCP/IP socket Verbindung auf einzelne Pakete in 
unter 10 Millisekunden eine Antwort zurück senden. Wie geht das mit der 
Socket API?


Mir ist absolut bewusst, dass sockets eine Abstraktion auf TCP/IP sind.

Bildlich gesprochen sind sockets für mich so etwas wie eine Röhre, in 
die man auf der einen Seite einen stream reingiesst und der trotz aller 
Unzulänglichkeiten der Verbindung auf der andere Seite wieder heraus 
kommt.

Auf socket-Ebene merkt man von der Paketierung in einzelne TCP/IP 
Pakete, retransmission etc. gerade nichts. Das soll ja auch so sein.


Jetzt möchte ich aber mit einem Gerät arbeiten, das zyklisch über eine 
TCP/IP Verbindung in Echtzeit (genauer unten) Antworten auf zyklische 
Anfragen benötigt.

* Ich muss dem Gerät einen TCP/IP Socket Server bereit stellen.

* Es verbindet sich zu mir per 3-way handshake (SYN, SYN-ACK, ACK).

* Dann sendet es eine XML-Nachricht über diese TCP/IP Socket Verbindung 
als ASCII String. Die Nachricht hat eine variable Länge, passt aber 
immer in ein MTU von 1500. Somit wird nicht fragmentiert und eine 
Nachricht entspricht genau einem TCP/IP Paket. (Routing wird aktiv 
verhindert, indem die Pakete ein TTL von 1 haben)

* Ich muss nun innerhalb von 10 Millisekunden eine Antwort über die 
socket Verbindung zurück senden. Sie ist wieder eine XML Nachricht und 
passt in genau ein TCP/IP Paket.

In meiner Applikation (PC, Linux, C++, Python, was auch immer) bekomme 
ich über das Socket-Interface aber natürlich keine genaue Information, 
wann das Paket eingegangen ist. Es ist eher zufällig, wann der 
entsprechende TCP Stack des OS entscheidet, die Applikation zu 
informieren.

Könnt ihr das Problem nachvollziehen und hättet Ideen oder Vorschläge, 
wie man das Problem elegant auf nicht-uC angeht?
Auf einem uC würde ich natürlich direkt in der IRQ Routine das 
antriggern können ...

Danke!

PS: Die Entscheidung für XML über TPC/IP socket ist nicht von mir, 
sondern vom Hersteller des Gerätes vorgegeben. Das ist leider gesetzt, 
auch wenn ich es bisher noch nicht ideal erachte was die Technologie 
angeht.

von Rolf M. (rmagnus)


Lesenswert?

TCP und XML sind für Echtzeit-Anwendungen mit kurzen Reaktionszeiten 
nicht sonderlich gut geeignet.

Sascha S. schrieb:
> In einer Applikation (x86 oder ARM Linux, nicht bare metal uC)
> muss ich innerhalb einer TCP/IP socket Verbindung auf einzelne Pakete in
> unter 10 Millisekunden eine Antwort zurück senden.

TCP ist eigentlich Stream-orientiert und nicht Datagranm-orientiert. Das 
heißt, wie die von dir übergebenen Daten in Pakete aufgeteilt und 
übertragen werden, ist Sache des IP-Stacks. Du kannst dich lediglich 
darauf verlassen, dass alle gesendeten Bytes in der richtigen 
Reihenfolge wieder ankommen.

> In meiner Applikation (PC, Linux, C++, Python, was auch immer) bekomme
> ich über das Socket-Interface aber natürlich keine genaue Information,
> wann das Paket eingegangen ist. Es ist eher zufällig, wann der
> entsprechende TCP Stack des OS entscheidet, die Applikation zu
> informieren.

Ja, weil eben TCP. Siehe Nagle-Algorithmus (den man allerdings auch 
abschalten kann).
Aber was genau brauchst du jetzt? Einen Zeitstempel der empfangenden 
Daten, oder dass sie schnellstmöglich in der Applikation ankommen?

> PS: Die Entscheidung für XML über TPC/IP socket ist nicht von mir,
> sondern vom Hersteller des Gerätes vorgegeben. Das ist leider gesetzt,
> auch wenn ich es bisher noch nicht ideal erachte was die Technologie
> angeht.

Ist es auch nicht.

von Herbert B. (Gast)


Lesenswert?

Es gibt ein paar Socketoptionen um Packete schneller zu handeln.
Nodelay und cork:
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux_for_real_time/7/html/reference_guide/chap-sockets

Ansonstnen kann man den Prozess noch auf eine CPU pinen, IRQ der NIC 
festnageln, realtimekernel, .... aber das wird wohl gar nicht notwendig 
sein und hilft auch nicht weiter wenn deine Verbindung schlecht/um den 
halben Erdball geht, dann werden die 10ms vermutlich öfters gerissen.

von Max D. (max_d)


Lesenswert?

10ms sind dankbarerweise "relativ" lang für so einen PC.
Mit etwas Glück und wenn dein System sonst nicht zu vollhängt kriegt das 
wsl eine ganz normale Applikation hin da zu reagieren...
Je nachdem wie kritisch die Anwendung ist würde ich das einfach mal 
probieren wo deine Reaktionszeiten da landen.

von Rolf M. (rmagnus)


Lesenswert?

Max D. schrieb:
> 10ms sind dankbarerweise "relativ" lang für so einen PC.

Ja, allerdings. Lesen und schreiben der Daten über den Socket ist in dem 
Fall nicht das Problem, sondern eben eher, dass man TCP so "verbiegen" 
muss, dass es nicht zu zu großen Verzögerungen im IP-Stack kommt und 
dass es etwas suboptimal ist, in einem (mehr oder weniger) 
Echtzeit-Kontext einen XML-Parser laufen zu lassen.

: Bearbeitet durch User
von Sascha S. (strongly-typed)


Lesenswert?

Merci für alle Antworten schon einmal.

Rolf M. schrieb:
> Ist es auch nicht.

Danke, dass Du meine Einschätzung teilst -)


Rolf M. schrieb:
> Aber was genau brauchst du jetzt? Einen Zeitstempel der empfangenden
> Daten, oder dass sie schnellstmöglich in der Applikation ankommen?

Ich benötige die Daten in der Applikation "so schnell wie möglich". 
Tatsächlich ist in den Daten bereits der Zeitstempel vom Absenden 
enthalten.


Herbert B. schrieb:
> aber das wird wohl gar nicht notwendig
> sein und hilft auch nicht weiter wenn deine Verbindung schlecht/um den
> halben Erdball geht, dann werden die 10ms vermutlich öfters gerissen.

Die Verbindung ist direkt, per "kurzem" Ethernetkabel von Buchse Gerät 
zu meinem PC / RPi. Routen geht eh nicht wegen TTL = 1. Maximal kommt 
ein Switch dazwischen, zur not cut-through statt store-forward.


Herbert B. schrieb:
> Es gibt ein paar Socketoptionen um Packete schneller zu handeln.
> Nodelay und cork:

Das hört sich gut an, schaue ich mir an!


Herbert B. schrieb:
> Ansonstnen kann man den Prozess noch auf eine CPU pinen, IRQ der NIC
> festnageln, realtimekernel,

"CPU pinen" hört sich gut an. Jemand Erfahrung damit?

"realtimekernel": Gerne auch: Gibt es da inzwischen etwas "fertiges" für 
ein RPi?

Max D. schrieb:
> 10ms sind dankbarerweise "relativ" lang für so einen PC.
> Mit etwas Glück und wenn dein System sonst nicht zu vollhängt kriegt das
> wsl eine ganz normale Applikation hin da zu reagieren...
> Je nachdem wie kritisch die Anwendung ist würde ich das einfach mal
> probieren wo deine Reaktionszeiten da landen.

Ich habe es einmal probiert. Mit einer interpretierten Sprache (hust) 
klappt es nicht. Mit Wireshark bin ich da bei ca. 15 bis 20msec 
gelandet. Bevor ich auf C / C++  Rust  whatever anfange, habe ich dann 
einmal angefangen über das eigentliche Problem nachzudenken. Ich darf 
tatsächlich 1% (Sample size 1000) der Antworten verspätet abliefern, 
daher eine "weiche" Echtzeitanforderung

von Sascha S. (strongly-typed)


Lesenswert?

Rolf M. schrieb:
> dass es etwas suboptimal ist, in einem (mehr oder weniger)
> Echtzeit-Kontext einen XML-Parser laufen zu lassen.

Das muss ich auch noch genau evaluieren. Das XML ist relativ statisch 
(ggf. sogar sehr statisch, d.h. Struktur fix und "nur" Messwerte 
innerhalb der Struktur variieren). Dann würde ich das parsen vom XML 
auch alt-griechisch von Hand mit ein paar geschickten strtok 
hinbekommen.

von Herbert B. (Gast)


Lesenswert?

Sascha S. schrieb:
> Ich habe es einmal probiert. Mit einer interpretierten Sprache (hust)
> klappt es nicht. Mit Wireshark bin ich da bei ca. 15 bis 20msec
> gelandet.
Wieviele XMLs schickt denn das Gerät? Kommt da ein Dauerstrom an XMLs 
oder ist das eine einmalige Aktion wo dann 1-? XMLs ankommen?

von Sascha S. (strongly-typed)


Lesenswert?

Herbert B. schrieb:
> Wieviele XMLs schickt denn das Gerät? Kommt da ein Dauerstrom an XMLs
> oder ist das eine einmalige Aktion wo dann 1-? XMLs ankommen?

Das Gerät schickt immer das selbe XML alle 12 Millisekunden, in dem nur 
die Messwerte verändert sind (strings von floats mit fester Anzahl an 
Nachkommastellen).

Das lässt sich denke ich händisch machen. Soweit ich es sehe, kann die 
Struktur des XML auch durch Konfiguration nicht verändert werden. Und 
wenn doch, ist das statisch, mit einem Neustart, verbunden. So dass auch 
der Server neu gestartet werden muss. Das kann also statisch, ggf. mit 
Codegenerator, erledigt werden IMHO.

Herbert B. schrieb:
> Sascha S. schrieb:
>> Ich habe es einmal probiert. Mit einer interpretierten Sprache (hust)
>> klappt es nicht. Mit Wireshark bin ich da bei ca. 15 bis 20msec
>> gelandet.

Tatsächlich klappt es auch zumindest testweise mit einer interpretierten 
Sprache mit `TCP_NODELAY`. Ich würde gerne aber noch das pinning und 
einen realtimekernel, z.B. auf einem RPi ausprobieren. Was schlagt ihr 
da vor?

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Muss es denn unbedingt TCP sein? (RTP/)UDP ist fuer so'ne Faxen deutlich 
besser geeignet. Und solange das jetzt nur "ueber ein kurzes Kabel" 
muss, duerfte dann auch eh' nix verloren gehen, d.h. der ganze TCP 
Sermon: "Wie isset? Ja, danke und Selbst? Mussja..." sollte 
ueberfluessig sein.

Gruss
WK

von Sheeva P. (sheevaplug)


Lesenswert?

Sascha S. schrieb:
> Das muss ich auch noch genau evaluieren. Das XML ist relativ statisch
> (ggf. sogar sehr statisch, d.h. Struktur fix und "nur" Messwerte
> innerhalb der Struktur variieren). Dann würde ich das parsen vom XML
> auch alt-griechisch von Hand mit ein paar geschickten strtok
> hinbekommen.

Deine Informationen sind leider etwas... spärlich, fürchte ich. Wie 
genau sehen die Anfragedaten aus, und wie genau die Antwortdaten? 
Brauchst Du die (alle?) Daten aus der Anfrage, um die Antwort zu 
erzeugen? Wann müssen die empfangenen Daten verarbeitet werden: sofort 
oder hat das Zeit? Wieviele Datenpakete empfängst Du in einem Zyklus?

Worauf ich abziele, ist, daß Du Deine Daten vielleicht erst 
zwischenspeichern könntest, wenn das Deine verfügbaren Speichermedien 
nicht sprengt. Wenn Du Daten aus dem Requestpaket brauchst, um die 
Response zu erzeugen, könntest Du diese womöglich selektiv 
"ausschneiden" und den Rest des Paketes erst einmal einfach in 
irgendeine Datenstruktur, vermutlich eine Liste schreiben, oder in einen 
Redis-Server, oder... und das könnte eventuell auch asynchron gemacht 
werden (etwa einer leichtgewichtigen Goroutine), nachdem Deine Antwort 
schon gesendet worden ist. Je nach Speichermedium könnte auch sinnvoll 
sein, nicht jedes Paket sofort zu schreiben, sondern in Chunks: erst n 
Pakete sammeln und dann alle zusammen "in einem Rutsch" abspeichern, bei 
einem blockorientierten Gerät also warten, bis genug Daten für einen 
Block vorhanden sind.

Nur so ein paar Ideen ins Blaue... HTH, YMMV.

von Peter (pittyj)


Lesenswert?

Meiner Meinung nach ist das Design hier nicht ausgereift.
Wer legt TCP/IP fest? Warum XML? Warum genau 10ms?
Da hat doch jemand Parameter festgelegt, die in der realen Welt nicht 
funktionieren. Sprich mit den Leuten.

Ich hätte UDP genommen, und auch mehr Zeit gegeben.

Ja, man kann Tricksen, dass man es mit Kernel Parametern, Tricky 
Programmierung etc hinbekommt. Aber das ist nicht wartungsfreundlich, 
portabel, erweiterbar.

von Herbert B. (Gast)


Lesenswert?

Sascha S. schrieb:
> Das Gerät schickt immer das selbe XML alle 12 Millisekunden, in dem nur
> die Messwerte verändert sind (strings von floats mit fester Anzahl an
> Nachkommastellen).
XML ist hier kein Flaschenhals da es ja in einee MTU reinpasst und 
simpel aufgebaut ist so dass man es ohne SAX-Parser, DOM-Geraffel 
auslesen kann.

Dein Hauptproblem ist TCP, das einfach viel Overhead erzeugt. JEdes 
deiner Minipakete erwartet ein ACKN-Antwort, dann schickst du selber 
nochmal eine Antwort das auch wieder ein ACKN erwartet, bei solchen 
Minipaketen erzeugst du unnötig viel Overhead wo evt. deine Maschine aus 
dem Tritt kommt weil deren TCP-IP Stack schon unter kleiner Last 
wackelt,... Nix genaues weiss man ja nicht was du da einsetzt.

Kannst ja noch einer Compilersprache herumexperimentieren da gewinnst du 
vielleicht beim Parsen noch etwas Zeit, mit CPU-Pininig, IRQ-Balancing 
abschalten, Realtimepatches,... würde ich es gar nicht erst versuchen, 
das bringt hier höchstwarhscheinlich nichts.

von Sascha S. (strongly-typed)


Lesenswert?

Sascha S. schrieb:
> auch wenn ich es bisher noch nicht ideal erachte was die Technologie
> angeht.

Dergute W. schrieb:
> Muss es denn unbedingt TCP sein? (RTP/)UDP ist fuer so'ne Faxen deutlich
> besser geeignet. Und solange das jetzt nur "ueber ein kurzes Kabel"

Da stimme ich Dir voll und ganz zu.

Peter schrieb:
> Da hat doch jemand Parameter festgelegt, die in der realen Welt nicht
> funktionieren. Sprich mit den Leuten.

Das ist leider hoffnungslos. Das Gerät ist von einem Konzern (> 1E9 EUR 
Umsatz) und das Produkt ist komplex und längst abgekündigt. An der Seite 
wird sich nichts ändern lassen. Die haben wahrscheinlich noch nicht 
einmal mehr die Quellen. Friss oder stirb ist die Devise dort leider.

Sheeva P. schrieb:
> Wann müssen die empfangenen Daten verarbeitet werden: sofort
> oder hat das Zeit? Wieviele Datenpakete empfängst Du in einem Zyklus?

Ich bekomme in einem Zyklus genau ein XML-Konstrukt in einem TCP-Paket, 
das ich "sofort" beantworten muss und die Antwort muss "sofort" (konkret 
<= 10ms nach Absenden) im Gerät als Antwort eingehen.

Peter schrieb:
> Ich hätte UDP genommen, [...]
Ja, siehe oben. In einer etwas neueren Version (die wahrscheinlich 
inkompatibel für mein Gerät ist) ist UDP eine Option. Das muss ich aber 
noch prüfen.

Am Ende ist es aber auch wieder der Netzwerkstack des OS, der nicht 
garantiert, dass die Applikation "sofort" das Paket bekommt.

Peter schrieb:
> [...] und auch mehr Zeit gegeben.
Das ist tatsächlich nicht möglich, da meine Antworten auf die 
Nachrichten in einen harten Echtzeitprozess (mit RTOS, Regler, etc., 
Zykluszeit 10ms) im Gerät eingreifen. Somit muss man schon "zügig" da 
eine Antwort liefern.

Das Parsen des XML ist gar nicht mein grösstes Problem. Da bin ich 
sicher, eine Variante zu finden, die schnell genug ist. Es ist auch eher 
ein String mit fester Struktur, der halt am Anfang, Ende und dazwischen 
das XML-Geraffel drin hat.

Mein Kernproblem ist der TCP/IP Stack, der ggf. verzögert die 
eingehenden Pakete an meine Applikation liefert. Da hat TCP_NODELAY 
schon eine Verbesserung gebracht. Da möchte ich aber noch sehen, wie ich 
das verbessern kann, damit ich mehr Zeit habe, mir meine Antwort zu 
berechnen. Diese hängt nämlich von den eingehenden Daten ab. Effektiv 
schleift man über diese Schnittstelle eine externes Gerät in die 
Echtzeitregelkreise des Zielgerätes ein.

von Sascha S. (strongly-typed)


Lesenswert?

Herbert B. schrieb:
> Kannst ja noch einer Compilersprache herumexperimentieren da gewinnst du
> vielleicht beim Parsen noch etwas Zeit,
Danke für Deine Einschätzung, ja da bin ich gerade dran.

> mit CPU-Pininig, IRQ-Balancing
> abschalten, Realtimepatches,... würde ich es gar nicht erst versuchen,
> das bringt hier höchstwarhscheinlich nichts.

Danke auch hier für Deine Einschätzung. Das wäre vermutlich ein rabbit 
hole, in dem ich verschwunden wäre.

Das Design und Technologieentscheidung ist ja nicht von mir, sondern wie 
gesagt von dem Hersteller. Wenn man mit einem bare-metal embedded System 
mit Ethernet-Schnittstelle auf das Gerät zugreift, hat man Ende der 90er 
vielleicht gar nicht einen ganzen TCP/IP Stack implementiert, sondern 
ggf. schon fast hard-coded "SYN, SYN/ACK, ACK".

Wenn man dann über die IRQ vom uC (oder gar externer Ethernet MAC) jedes 
Paket einzeln erhält, kann man die Daten eigentlich ganz bequem 
rechtzeitig abholen und beantworten. Und das ganze mit XML aufzublähen 
passt dann als Managemententscheidung zu Anfang der 2000er, damit man 
auch "innivotiv" ist.

Man kann daraus nur lernen, wie man es besser nicht macht .-) und ein 
paar Tricks entwickeln, trotzdem damit zurecht zu kommen und das Ziel zu 
erreichen.

von G. K. (zumsel)


Lesenswert?

TCP bringt im Zweifel Verzögerungen im Minutenbereich.

Was passiert eigentlich wenn der Sender sein Zeug nicht los wird, wie 
lange dauert es bis eine neue Verbindung versucht wird?

von Peter (pittyj)


Lesenswert?

Wenn man die Zeiten und TCP nicht ändern kann:
Ich spiele gerade mit einem STM32H743 mit Ethernet herum.
Der Prozessor selber kann auch Ethnernet. Kommt ein Paket an, dann gibt 
es einen Interrupt, und man kann sofort das Paket analysieren, und 
irgendwas zurück schicken. Da ist dann kein Delay oder Taskwechsel 
dazwischen.

STM liefert FreeRtos und einen LW IP-Stack mit. Geht jedoch auch ohne, 
man kann nackte Pakete schicken.

Ist Bare-Metal Programmierung ohne OS. Muss man können/mögen.

von J. S. (jojos)


Lesenswert?

Es gibt zwei Bremsen, einmal den Nagle Algorithmus der ja schon genannt 
wurde. Da wartet der Sender wenn nur wenig Daten zu Senden sind bis es 
lohnt. Das kann mit der Socket Option TCP_NODELAY beeinflusst werden.
Dann gibt es noch Empfangsseitig ein delayed Ack. Hier kann der 
Empfänger mehrere bis zig ms warten bevor er ein Ack an den Sender 
zurückschickt. Das kann der Sender nicht beeinflussen und es hängt vom 
OS ab ob und wie lange ein Ack verzögert wird.
Also von daher ein Ping Pong mit kleinen Anfragen/Antworten vermeiden.

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

Ist denn die Antwort abhängig vom Inhalt des gerade empfangenen Paketes? 
Ansonsten könnte man diese quasi "vorfertigen", damit sollte sich der 
Aufwand für die Antwort trotz XML in Grenzen halten ...

von Thorsten M. (pappkamerad)


Lesenswert?

Hier, siehe Link, hat einer einen Ethercat-Master auf einem Raspi 
implementiert, vielleicht kannst du (TE) dir da ein paar Ideen holen. 
Ethercat nutzt nicht TCP sondern nackte Ethernet-Pakete, aber er hat 
anscheinend harte Echtzeit hinbekommen.

http://www.simplerobot.net/2021/04/picat4-fast-prototyping-four-wheeled.html

von Patrick B. (p51d)


Lesenswert?

Sascha S. schrieb:
> TLDR: In einer Applikation (x86 oder ARM Linux, nicht bare metal uC)
> muss ich innerhalb einer TCP/IP socket Verbindung auf einzelne Pakete in
> unter 10 Millisekunden eine Antwort zurück senden. Wie geht das mit der
> Socket API?

Wurde ja schon diverses genannt. Aber 10ms sind jetzt wirklich nicht 
eine Herausforderung. Das Linux mit einem RT-Patch versehen und dann 
kannst du ja das Socket pollen. Besser auf UDP umsteigen.

Oder wenn du ganz tief und hardware nah sein möchtest -> XDP

Eventuell kannst du ja bei den Socket-Implementationen von Open62541 
etwas abschauen

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.