19.07.2010, 09:36
Beitrag #1
|
Raijin
LVF-Grünschnabel
Beiträge: 13
Registriert seit: Nov 2008
8.6 Base
2008
de
22145
Deutschland
|
Speicher voll: Server-Client-Anwendung (UDP)
Moin moin,
derzeit bin ich damit beschäftigt, unseren Temperaturlogger neu zu programmieren. Dazu habe ich eine Server-Anwendung erstellt, die von der Hardware einmal pro Minute die 16 Kanäle abfragt und in einer CSV-Datei abspeichert. Die Clients melden sich beim Start beim Server an und erhalten dann ebenfalls einmal pro Minute die Messwerte (vom Server, nicht von der Hardware). Allerdings sollen die letzten 5 Tage vom Server (1440 Minuten pro Tag * 5 Tage * 16 Kanäle = 115200 Messwerte) beim Client direkt angezeigt werden. Das heißt ich muss bei der Initialisierung des Clients recht viele Daten übertragen. Es werden vermutlich 3-5 Clients gleichzeitig aktiv sein.
Meine Frage ist nun: Wie mache ich das am besten? Das An- und Abmelden der Clients sowie das minütliche Verteilen der aktuellen Messwerte geschieht über UDP. Der Server speichert dann den Client samt IP und Port in einer FGV zur Weiterverarbeitung in den Daten-VIs. Wenn ich nun die besagten 115200 Messwerte in 8kb-große Pakete zerteile und per UDP rüberschicke, dann müllt mir der Client beim Empfang den Arbeitsspeicher mit 200-300 MB zu. Mitunter geht das soweit, dass ich eine Meldung bekomme, dass der Speicher voll ist. Das kanns ja irgendwie nicht sein. Leider habe ich nur das Base-Paket von Labview und mir stehen dementsprechend auch nur begrenze Mittel zur Verfügung (z.B. keine Event-Struktur).
Die Datenpakete, die ich vom Server zu den Clients schicke, sehen ungefähr so aus:
INIT_[KanalNr]_[PaketNr]_Wert1_Wert2_Wert3_....
Das ganze dann soweit bis die maximale Paketlänge von 8192 Bytes voll ist (genau genommen mache ich bei 8000 zu). Am anderen Ende werden diese Strings dann in ein Schieberegister gesteckt und "wenn alles da ist" wird es wieder auseinandergedröselt und in Signalverläufe gepackt (t0 und dt kommen in einem separaten Startpaket). Prinzipiell funktioniert die Datenkommunikation, die Pakete kommen an, werden zusammengesetzt und angezeigt.. Insgesamt werden das um die 100 UDP-Pakete. Das macht knapp 800 kb und dürfte für den Arbeitsspeicher "eigentlich" kein Problem sein.
Ich würd hier zwar gern meine VIs hochladen, aber das Projekt ist ziemlich groß und mit etlichen SubVIs. Ganz abgesehen davon dass ich das Firmenrechtlich gar nicht dürfte
Daher versuche ich einfach ein bischen Inspiration, ein paar Tips abzugreifen. Eine Idee war ursprünglich, die CSV-Dateien zu übertragen, aber da ich die Daten wirklich nur auf dem Server und keine x Kopien der CSV-Dateien auf den Clients haben will, hatte ich diesen Weg vorerst ausgeschlossen. Aber ich lasse mich gerne überzeugen.
Ist die Kommunikation über TCP evtl. besser? UDP verwende ich eigentlich nur, weil die ersten Beispiele, die ich fand, über UDP liefen.
|
|
|
19.07.2010, 10:40
Beitrag #2
|
nookie
CLA
Beiträge: 47
Registriert seit: May 2008
2011
2004
EN
2540
Oesterreich
|
Speicher voll: Server-Client-Anwendung (UDP)
Hallo,
du solltest dir einmal überlegen, wie du die Datenmenge reduzieren kannst. Brauchst du wirklich die volle Messauflösung von einem Wert pro Minute bei allen Clients?
Weiters kannst du dir überlegen, die Daten vor dem Übertragen in z.B. HEX zu konvertieren. Ich übertrage ein 30.000 Elemente fassendes I16 Array in unter einer halben Sekunde, indem ich die Zahlenwerte vor dem Übertragen in HEX umwandle. Schließlich kannst du ja nur ASCII Codes senden!
Wenn du einmal die Daten "komprimiert" hast zerteil den String in Pakete und sende eins nach dem anderen. Vergiss nicht ein definiertes Endepaket zu integrieren.
lG nookie
snip09
snip09
Intuition ist die Fähigkeit, die Lage in sekundenschnelle falsch einzuschätzen.
|
|
|
19.07.2010, 10:49
Beitrag #3
|
nookie
CLA
Beiträge: 47
Registriert seit: May 2008
2011
2004
EN
2540
Oesterreich
|
Speicher voll: Server-Client-Anwendung (UDP)
' schrieb:Ist die Kommunikation über TCP evtl. besser? UDP verwende ich eigentlich nur, weil die ersten Beispiele, die ich fand, über UDP liefen.
Das kommt darauf an, was du haben willst.
TCP ist Verbindungsorientiert
UDP ist Verbindungslos
D.h. wenn du bei UDP ein Paket während der Übertragung verlierst, ist es weg, da der Empfang der Pakete nicht bestätigt wird.
lG nookie
Intuition ist die Fähigkeit, die Lage in sekundenschnelle falsch einzuschätzen.
|
|
|
20.07.2010, 09:38
Beitrag #4
|
Raijin
LVF-Grünschnabel
Beiträge: 13
Registriert seit: Nov 2008
8.6 Base
2008
de
22145
Deutschland
|
Speicher voll: Server-Client-Anwendung (UDP)
Hey, danke für den Tip! Hätte ich eigentlich auch selber drauf kommen können, die Messwerte so zu übertragen *AscheAufMeinHaupt*
Deinen Vorschlag habe ich aufgegriffen und ein bischen rumprobiert. Rausgekommen ist das hier (Kommentare/Kritik erwünscht):
Heute oder morgen baue ich das in mein Programm ein und werde hier Bericht erstatten. Wobei ich die N-Berechnung für die Decodierschleife noch rausnehmen werde. Stattdessen steht dann im Paketheader die Datenlänge. Hoffentlich hat sich mein Problem damit erledigt.
Zu TCP/UDP: Du meinst also TCP wäre da die bessere Wahl? Habe mich da wie gesagt nicht festgelegt, war nur das erste was ich gesehen und probiert habe. Ich werd mal die Examples durchstöbern und mir TCP angucken.. Gibt es dabei irgendwas spezielles zu beachten im Unterschied zu UDP?
|
|
|
20.07.2010, 14:27
(Dieser Beitrag wurde zuletzt bearbeitet: 20.07.2010 15:05 von nookie.)
Beitrag #5
|
nookie
CLA
Beiträge: 47
Registriert seit: May 2008
2011
2004
EN
2540
Oesterreich
|
Speicher voll: Server-Client-Anwendung (UDP)
' schrieb:Zu TCP/UDP: Du meinst also TCP wäre da die bessere Wahl? Habe mich da wie gesagt nicht festgelegt, war nur das erste was ich gesehen und probiert habe. Ich werd mal die Examples durchstöbern und mir TCP angucken.. Gibt es dabei irgendwas spezielles zu beachten im Unterschied zu UDP?
Du hast eine Server-Multiclientanwendung. Also hast du Ansich mit UDP Multicast eine gute Wahl getroffen. Eine auf TCP Basierte Server-Multiclienanwendung ist nicht mehr ganz so trivial wie die Lösung mit UDP Multicast. Was du beachten musst ist, das es spezielle Multicast IP-Adressen gibt. In IPv4 ist hierfür der Adress-Bereich 224.0.0.0 bis 239.255.255.255 vorgesehen. LabVIEW Standardadresse in den Beispielen ist 234.5.6.7.
Eine frage noch an dich: Welche Fehlernummer hast du bekommen? Zufällig die 61? Ich hab nämlich heute das gleiche Problem bekommen, als ich an meinem Projekt weiterentwickeln wollte. Blöderweise bin ich gerade im Krankenhaus und hab keine Netzwerkverbindung außer über das Handynetz. Kann also sein, das UDP einen Fehler liefert, wenn keine Netzwerkverbindung verfügbar ist.
Die Codierung und Decodierung sollte vom prinzip her passen. Schau halt, dass du noch ein definiertes Ende Packet hinzufügst, damit du den HEX-String am anderen Ende wieder richtig zusammensetzen und interpretieren kannst.
lG nookie
Intuition ist die Fähigkeit, die Lage in sekundenschnelle falsch einzuschätzen.
|
|
|
20.07.2010, 16:19
Beitrag #6
|
Raijin
LVF-Grünschnabel
Beiträge: 13
Registriert seit: Nov 2008
8.6 Base
2008
de
22145
Deutschland
|
Speicher voll: Server-Client-Anwendung (UDP)
UDP-Multicast:
Naja, ganz so einfach ist das nicht. Meine Versuche mit Multicast sind gescheitert. Ich nehme mal an, dass unsere IT da irgendwas blockiert. Aber ich möchte vermeiden, unsere IT-Leute mit ins Boot zu holen, weil die eh nix selbst dürfen und gleich bei der Konzernmutter auf der Matte stehen müssen, um sich temporäre Berechtigungen zu besorgen. Daher merkt sich mein Server die Clients, die sich bei ihm "anmelden" in einem simplen Array mit Client-IP und Client-Port. Die Senderoutine wird per Message-Queue angestoßen und schickt anhand der Nachricht in der Q Daten an Client xy. Bei max 5 Clients und lediglich einem Update pro Minute (1 Paket mit wenigen Bytes) ist das aber halb so wild.
Letztendlich dürfte sich also nicht soviel ändern, wenn ich das auf TCP umstricke, weil die Senderoutine dann eben einfach auf TCP sendet und nicht mehr auf UDP. Allerdings muss ich mich noch mit den Unterschieden im Detail auseinandersetzen. Zum Beispiel inwiefern ich TCP-Connections in einem Array festhalten kann/muss oder dergleichen...
Fehlernummer:
Das kann ich dir ehrlich gesagt nicht sagen. Ich teste die "verteilte" Anwendung derzeit quasi "unverteilt". Ich starte also an meinem PC gleichzeitig Server und Client und schicke mir selbst munter Nachrichten ^^ Zwischendurch teste ich das allerdings mit nem Kollegen im Büro. Eine Fehlernummer hatte ich gar nicht. Es trat nur das Phänomen auf, wenn ich vom Client aus eine Dateninitialisierung angestoßen habe ("Gib mir alles"), kam zwar soweit alles rüber, aber irgendwann beim zweiten, dritten, vierten Klicken (auch nach längerer Pause) kam plötzlich eine Fehlerbox "Speicher voll". Ein Blick in den Taskmanager zeigte dann, dass der Speicherbedarf nach einem Senden um schlanke 200-300 MB hoch ging. Ich vermute mal stark, dass das auch oder gar zum Großteil mit meiner alten Decodierung zu tun hatte und LV mir den Speicher mit Dutzenden oder gar Hunderten Kopien der Daten zugemüllt hat. Rein rechnerisch wären die 100 Pakete (vor Byte-Konvertierung) zu jeweils 8 KB nämlich gerade mal 800 KB und keine 200-300 MB. Wenn da aber munter Kopien erzeugt werden, die der Garbage-Collector dann nicht mehr aufräumt, ist der Speicher halt irgendwann voll. Ich erhoffe mir aber durch die schlauere Codierung nun auch eine schlauere Decodierung und bin das Problem dann hoffentlich los
Ende-Paket:
Bisher hab ich im Startpaket einfach die Anzahl der Pakete (neben t0, dt, Anzahl Kanäle, etc) mit übergeben. Sogesehen wüsste der Empfänger also wieviele Pakete er zu erwarten hat. Wenn ein Datenpaket sonst durch irgendwelche Netzwerk-Engpässe nen Tick später kommt als das Ende-Paket ist im wahrsten Sinne des Wortes Ende im Gelände. Wobei ich mir nicht sicher bin inwiefern TCP da jetzt sofort Alarm schlagen würde (von wegen "Paketsicherheit" und so).
Oha, du liegst im Krankenhaus? Na dann mal gute Besserung!
|
|
|
22.07.2010, 09:39
Beitrag #7
|
Raijin
LVF-Grünschnabel
Beiträge: 13
Registriert seit: Nov 2008
8.6 Base
2008
de
22145
Deutschland
|
Speicher voll: Server-Client-Anwendung (UDP)
Ich habe nun erfolgreich auf TCP umgestellt. Zwar muss man sich dann mehr mit Längenbytes und Headern rumschlagen (und dadurch auch mit mehreren Lese-Operationen), aber letztendlich spart man sich das mühsame aufsplitten der Daten auf xx UDP-Pakete. Einfach einen String mit allem drum und dran zusammenbasteln und den dann mit einem TCP-Sendebefehl abschicken. Der Client liest dann Stück für Stück aus dem TCP-Stream und decodiert das ganze. Mein Speicherproblem ist als netter Nebeneffekt auch eliminiert worden - was auch immer nu tatsächlich die Ursache war.
"Kleine" Optimierung:
Ich habe die Schleife, die aus dem U8-Array ein U16-Array bastelt rausgenommen. Bei 115k Iterationen dauert diese Schleife knappe 7 Sekunden. Stattdessen habe ich mit "Array dezimieren" gearbeitet und so jeden 2. Wert in ein separates U8-Array gepackt. Dadurch habe ich dann jeweils ein U8-Array für die oberen und die unteren Bytes. Das ganze kombiniert mit "Zahl verbinden" resultiert in einer Durchlaufzeit von 0,02 Sekunden
Auf dem folgenden Bild ist die alte Variante oben, die neue unten..
Wenn der Client jetzt ein Update der Daten anfordert, ist eine Verzögerung kaum noch spürbar.
|
|
|
22.07.2010, 10:44
Beitrag #8
|
nookie
CLA
Beiträge: 47
Registriert seit: May 2008
2011
2004
EN
2540
Oesterreich
|
Speicher voll: Server-Client-Anwendung (UDP)
Hello,
falls du die Funktion noch nicht kennst, du kannst eine normale For-Loop auch gleichzeitig parallel auf mehreren Logischen CPU-Kernen ausführen. Da du ja jedes Array-Element für sich betrachtest, wäre das ja an sich kein Problem.
Somit könntest du noch einiges an Performance herausholen!
snip09
lG nookie
Intuition ist die Fähigkeit, die Lage in sekundenschnelle falsch einzuschätzen.
|
|
|
22.07.2010, 11:25
(Dieser Beitrag wurde zuletzt bearbeitet: 22.07.2010 11:27 von GerdW.)
Beitrag #9
|
GerdW
______________
Beiträge: 17.469
Registriert seit: May 2009
LV2021
1995
DE_EN
10×××
Deutschland
|
Speicher voll: Server-Client-Anwendung (UDP)
Hallo nookie,
bist du dir sicher, dass du beim Umwandeln von [I16] nach String wirklich den Umweg über ein (großes) Bitarray gehen musst?
Ich mache sowas üblicherweise so (gleich mal 2 Möglichkeiten):
|
|
|
| |