Protokoll für Clients....

Alle Fragen zur Netzwerkkommunikation
pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Protokoll für Clients....

Beitrag von pluto »

Hallo

Seit längerer Zeit arbeite ich an einem Server/Client Projekt. Es gibt also ein Server und inzwischen mehrer Clients.

Die Clients Senden Information an den Server, der Server Verteilt sie an alle anderen Clients.
Ohne jetzt auf die Clients im einzelnen einzugehen, würde ich gerne das Format vom jetzigen Protokoll ändern.
Es gab schon Änderungen früher, aber nun möchte ich mir noch mal andere Ideen anschauen.

Im Moment sieht das Protokoll so aus:

Code: Alles auswählen

 
/Arbeitzimmer/Regal/BPI/MPD/Playing/600/stop
/Arbeitzimmer/EckeFenster/OnlyTemp/Draußen/Temperatur/250/9.50 *C
/Arbeitzimmer/EckeFenster/OnlyTemp/Dachboden/Temperatur/251/11.50 *C
/Arbeitzimmer/EckeFenster/OnlyTemp/Arbeitzimmer/Temperatur/252/19.00 *C
/Arbeitzimmer/Fenster/OnlyTempWindowStatus/status/2510/Fenster zu
/Schlafzimmer/Nachtregal/LCDModul1Temp/Temperatur/260/18.00 *C
/Schlafzimmer/Nachtregal/LCDModul1TempLuft/Luftfeuchtigkeit/2620/55.70 %
/Schlafzimmer/Nachtregal/LCDModul1TempLuft/Temperatur/2621/17.80 *C
/Arbeitzimmer/Schreibtisch/LCDModul3TempLuft/Luftfeuchtigkeit/280/53.0 %
/Arbeitzimmer/Schreibtisch/LCDModul3TempLuft/Temperatur/281/19.9 *C
/Arbeitzimmer/Schreibtisch/LCDModul3TempDruck/Luftdruck/2820/1004.01 hpa
/Arbeitzimmer/Schreibtisch/LCDModul3TempDruck/Temperatur/2821/20.29 *C
/Arbeitzimmer/Regal/BPI/SystemInfo/CurrTemp/551/36.2 *C
/Arbeitzimmer/Regal/BPI/SystemInfo/CurrCurrent/552/663 mA
/Arbeitzimmer/Regal/BPI/SystemInfo/CurrVoltage/553/4.991 V
/Arbeitzimmer/Regal/BPI/SystemInfo/upTime/555/34T 21:07
/Arbeitzimmer/Schreibtisch/AudioSwitch/VolumeTDA1/3521/60 db
/Arbeitzimmer/Schreibtisch/AudioSwitch/VolumeTDA2/3522/60 db
/Arbeitzimmer/Schreibtisch/AudioSwitch/BassTDA1/3531/3 db
/Arbeitzimmer/Schreibtisch/AudioSwitch/BassTDA2/3532/3 db
/Arbeitzimmer/Schreibtisch/AudioSwitch/TrebleTDA1/3541/3 db
/Arbeitzimmer/Schreibtisch/AudioSwitch/TrebleTDA2/3542/3 db
/Arbeitzimmer/Schreibtisch/AudioSwitch/Input/355/2
/Arbeitzimmer/Schreibtisch/AudioSwitch/Output/356/1
/Arbeitzimmer/Regal/BPI/MPD/SongLen/601/04:12
/Arbeitzimmer/Regal/BPI/MPD/SongPos/602/00:00
/Arbeitzimmer/Regal/BPI/MPD/PlayFile/603/Royalty_Free?Neu02?Baba Yaga.mp3
/Modul/pmpdApp/PlayPause
/Modul/pacAPP/Serial/35,CMD,OUT,1
/Arbeitzimmer/Regal/BPI/MPD/Title/604/Baba Yaga
/Arbeitzimmer/Regal/BPI/MPD/Artist/605/Kevin MacLeod
 

Probleme sind z.b. das der Separator natürlich auch in Pfad angeben vorkommt.
Außerdem kann ich die Zeilen nur von Hinten lesen, von hinten sind sie klar strukturiert von vorne nicht.
Ich möchte auch ein Zeitstempel haben.

Darum habe ich mir ein neues Format überlegt, was mir aber etwas übertrieben vorkommt:

Code: Alles auswählen

 <Info TimeStep="..." Ort="/Arbeitzimmer/EckeFenster" Modul="OnlyTemp" Alias="Draußen" Property="Temperatur" Id="250" Value="9.50" Unit="*C">
  <Info TimeStep="..." Ort="/Arbeitzimmer/Regal/" Modul="BPI" Client="MPD" Property="Playing" Id="600" Value="stop">
 
  <Modul Target="pmpdApp" Value="PlayPause">
  <Modul Target="pacAPP" Value="Serial/35,CMD,OUT,1">
 

Wie ihr sehen könnt, hätte es mehrer Vorteile:
1. Es gibt ein "Zeitstempel"
2. Man könnte beliebig viele Parameter hinzufügen, wenn nötig.
3. Die Parameter könnten, in einer Beliebigen Reihenfolge angeordnet werden.

und hier kann man sehr schön sehen, zwischen den Clients gibt es über den Server auch schon eine "Unterhaltung".
pmpdAPP ist ein Client für den MPD-Server.
pacAPP ist der Arduino Client, der unter anderem mein Audio Umschalter steuert, der an meinem PC hängt.

hätte also einige Vorteile, aber es gibt natürlich viel Overhead.

Wie würdet ihr vorgehen?
MFG
Michael Springwald

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Protokoll für Clients....

Beitrag von Warf »

Wenn die hauptsächliche Kommunikation in einem privaten Netzwerk läuft, und nicht über das Internet, ist der overhead eigentlich komplett egal. Selbst ein avr Controller wie ein arduino sollte in der Lage sein ein xml Dokument in sehr kurzer Zeit zu parsen. Auch über das Internet wäre es theoretisch kein Problem (im Vergleich dazu das einige Chat Services 60+ Byte overhead generieren um 1 Byte statusmessages zu versenden), aber man muss die Router ja nicht mehr belasten als nötig, da sollte man sich überlegen das ganze eventuell zu optimieren, aber Lokal muss man sich gar keine Gedanken um overhead machen.

Ich würde das ganze eh komplett anders angehen, ich würde die ganzen Orte bzw. strings in einem dictionary indizieren, und dann deine strings durch Indizes ersetzen, und dann die Informationen als Record speichern. Das ist natürlich deutlich performanter und reduziert die geöße sehr stark.

Und für den broadcast könnte man sogar die IPv6 Multicast Funktion verwenden, sofern der Router das unterstützt, das wäre mit Abstand das performanteste

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Protokoll für Clients....

Beitrag von pluto »

Ich würde das ganze eh komplett anders angehen, ich würde die ganzen Orte bzw. strings in einem dictionary indizieren, und dann deine strings durch Indizes ersetzen, und dann die Informationen als Record speichern. Das ist natürlich deutlich performanter und reduziert die geöße sehr stark.

hast du dazu mal ein Beispiel zur Hand? Klingt jedenfalls Interessant.
MFG
Michael Springwald

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Protokoll für Clients....

Beitrag von pluto »

Du meinst es bestimmt ungefähr so:
Ich habe so eine liste ungefähr:

1 = /Arbeitzimmer/EckeFenster/OnlyTemp/Draußen/Temperatur/250/
2 = ....
3 = ...

Nun sende ich nur noch:
1:/9.50 *C

Das wäre natürlich auch eine Idee.
MFG
Michael Springwald

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Protokoll für Clients....

Beitrag von Warf »

Ja also z.B. der Client verbindet sich das erste mal mit dem Server, dann machst du sehr wahrscheinlich ja einen Handshake, bei dem sich der Client registriert. Da könnte der Client einfach eine Anfrage an den Server stellen so etwa "Hey ich bin Client xyz in Ort uvw" und der Server antwortet dann mit "Deine ID ist X und dein Ort ist Index Y" (mit X, Y dann natürlich als integer). Somit sparst du dann bei der Kommunikation den String overhead, bist aber nicht an ein Festes Dictionary gebunden.
Die Übertragung würde ich dann als Record vornehmen, wenn die größten stings eliminiert sind, und der rest sich als feste strings (z.B. Shortstrings) realisieren lässt:

Code: Alles auswählen

TInfoMessage = record
  ...
end;
TModulMessage = record
  ...
end;
 
TMessageType = (mtInfo, mtModul);
 
TTransmitMessage = record
  case MType: TMessageType of
    mtInfo: (IMsg: TInfoMessage);
    mtModul: (MMsg: TModulMessage);
end;


damit kannst du jede Message als TTransmitMessage darstellen, und über das MType Feld feststellen was für daten enthalten sind.
Die Message zu versenden ist damit auch kinderleicht (Beispiel für Unix Sockets senden mit Send):

Code: Alles auswählen

fpsend(Socket, @msg, SizeOf(Msg), 0); // angenommen msg sei eine Variable vom Typ TTransmitMessage


Durch einführen eines Größenfeldes im Record kannst du ihn im Nachhinein auch noch vergrößern ohne dabei die abwärtskompatiblität zu verlieren z.B.

Code: Alles auswählen

TTransmitMessage = record
  Size: UIntPtr;
  // das zeug von oben
end;
 
...
 
procedure ReadMessage(Socket: TSocket);
var sz: UIntPtr;
  msg: TTransmitMessage;
begin
  fpRecv(Socket, @sz, sizeof(sz), MSG_PEEK); //MSG_PEEK um das gelesene nicht gleich aus dem Buffer zu löschen
  FillChar(msg, SizeOf(msg), #00); // nicht benötigte Felder mit 0 initialisieren
  fpRecv(Socket, @msg, sz, 0); // MessageVersionSize sei ein
end;


Wenn du also neue Felder hinzufügst (also unter den Record schreiben) Vergrößert sich natürlich der Record, da man diese allerdings mit 0 initialisiert bleibt die Abwärtskompatiblität.

Wenn man es noch besser machen will kann man statt der größe eine Versionsnummer speichern, und je nach Versionsnummer einfach einen anderen Record Typen nehmen. Damit wäre nicht nur das vergrößern, sonder auch das Umstrukturieren und verkleinern im Nachhinein möglich. Das ist aber natürlich deutlich komplizierter als das ganze über die Größe zu machen

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Protokoll für Clients....

Beitrag von pluto »

Vielen Dank für das Beispiel...

Ich werde dazu mal eine neue Test Umgebung aufmachen.

Ich habe mir im Moment folgendes überlegt:
Jedes Modul hat seine eigene Config Datei, bei dem Arduino App mache ich das schon so. Dann eben auch auf Client ebene:

Code: Alles auswählen

 
OnlyTemp {
  App:pacAPP
  Ort:/Arbeitzimmer/EckeFenster/
  ParameterList {
    250:Draußen,Temperatur,°C
    251:Dachboden,Temperatur,°C
    252:Arbeitzimmer,Temperatur,°C
    2510:Status,Fenster,-
  }

}
 


Nun würde der Client das machen:
Beim Verbinden würde er sein Name Preis geben und der Server schaut nach, ob er schon so eine Liste hat. So braucht der Client das nicht immer zu Senden.
Wenn es eine neue Information gibt, könnte man noch ein Flag mit geben.

Das Senden würde ich dann so machen:
09.03.2017,21:02:20,250,7.00

Das wäre dann Extrem Sparsam. Das ganze könnte ich noch mit der record Variante Kombinieren.
Dann hätte ich ein Feld von TDate und eins von TTime und eins für die ID sowie ein Value, wobei der Value eigentlich keine feste Länge haben soll.
MFG
Michael Springwald

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Protokoll für Clients....

Beitrag von Warf »

pluto hat geschrieben:Vielen Dank für das Beispiel...

Ich werde dazu mal eine neue Test Umgebung aufmachen.

Ich habe mir im Moment folgendes überlegt:
Jedes Modul hat seine eigene Config Datei, bei dem Arduino App mache ich das schon so. Dann eben auch auf Client ebene:

Code: Alles auswählen

 
OnlyTemp {
  App:pacAPP
  Ort:/Arbeitzimmer/EckeFenster/
  ParameterList {
    250:Draußen,Temperatur,°C
    251:Dachboden,Temperatur,°C
    252:Arbeitzimmer,Temperatur,°C
    2510:Status,Fenster,-
  }

}
 


Nun würde der Client das machen:
Beim Verbinden würde er sein Name Preis geben und der Server schaut nach, ob er schon so eine Liste hat. So braucht der Client das nicht immer zu Senden.
Wenn es eine neue Information gibt, könnte man noch ein Flag mit geben.

Das Senden würde ich dann so machen:
09.03.2017,21:02:20,250,7.00

Das wäre dann Extrem Sparsam. Das ganze könnte ich noch mit der record Variante Kombinieren.
Dann hätte ich ein Feld von TDate und eins von TTime und eins für die ID sowie ein Value, wobei der Value eigentlich keine feste Länge haben soll.


Nun wenn du halt keinen fixen String verwendest wird das ganze natürlich deutlich komplizierter, die record Variante kannst du dann zum teil in die Tonne kloppen, da du den String separat versenden musst, und in Varianten records keine variablen von Typen die initialisiert werden müssen (dynamische Arrays, Strings) vorkommen dürfen.

Dann würde ich die Invarianten Datentypen in einen Record packen und dann ein Feld Contentsize hinzufügen und darin die länge des darauf folgenden strings speichern:

Code: Alles auswählen

TTransmitMessage = record
  //Invariante daten
  ContentLength: UintPtr;
end;
 
TMessage = record
  Head: TTransmitMessage;
  Content: String;
end;
 
...
function ReadMessage(s: Socket): TMessage;
begin
  fprecv(s, @Result.Head, SizeOf(TTransmitMessage), 0);
  SetLength(Result.Content, Result.Head.ContentLength);
  fprecv(s, PChar(Result.Content), Result.Head.ContentLength, 0);
end;


Das lässt sich dann natürlich wieder mit meinen vorschlagen von vorher kombinieren

Einen ähnlichen Ansatz hat auch HTTP/2 für die Komprimierung, Headerfelder werden das erste mal als String übertragen, danach fügt Server und Client sie zum dictionary hinzu, und jedesmal wenn dieser string wieder versendet werden würde wird einfach der Dictionary index (der ja auf Client und Server gleich ist) angegeben. Somit wird für stetig wieder auftauchende Texte viel bei der Übertragung gespart. (z.B. die ServerURL wird ja bei jedem Request neu gesendet, die ändert sich aber meistens nicht, daher kann man der dann einfach einen Index geben)

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Protokoll für Clients....

Beitrag von pluto »

Dann würde ich die Invarianten Datentypen in einen Record packen und dann ein Feld Contentsize hinzufügen und darin die länge des darauf folgenden strings speichern:

Den Begriff "Invarianten Datentypen" kenne ich nicht, aber ich schließe aus dem Code und deiner Aussage das du damit folgendes meinst:
Alle Datenfelder die eine Feste Länge haben können landen in "TTransmitMessage".
z.b.
Die ID, den Ort, ....:

Code: Alles auswählen

 
TTransmitMessage = record
  //Invariante daten
  Ort:String[20]; // Ich arbeite Selten mit Festen längen.
  ModulName:String[10];
  ContentLength: UintPtr;
end;
 

Bei meiner neuen Idee, gehe ich natürlich ein schritt Weiter:
Der Client würde sich beim Server "Anmelden" und wenn der Server den Client noch nicht kennt, würde der Server den Client Fragen: Wer bist du?
Andersherum: Wenn der Client neue Datenfelder hat, würde er dem Server mitteilen: Ich habe neue Datenfelder.
Weil ich möchte ja die Infos zu jeder ID speichern.
MFG
Michael Springwald

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Protokoll für Clients....

Beitrag von pluto »

Ich habe noch mal eine PDF Datei erstellt, wie ich es mir ungefähr vorstelle.
Auf den Seiten 1 und 2 findet ihr die erste neue Variante. D.h. Die Information landen einfach in einer Großen liste.
D.H. der Server liest beim Start eine Datei ein. Das wäre erst mal eine einfache Variante.
Die Daten könnten natürlich auch jedes mal vom Client gesendet werden beim Anmelden....
das wäre dann wahlweise z.b. bei neuen Clients der Fall oder bei Clients die noch in der Entwicklung sind.

Auf der letzten Seite gibt es noch eine etwas sparsamere Variante, so ähnlich mache ich es bereits im Arduino Client.
Die zweite Variante, würde die Information mehr zusammenfassen, es gebe weniger Redundanzen.

D.H. die Clients senden dann nur noch den Zeitstempel, eine ID und die Eigentliche Information.
Der Zeitstempel spielt für mich eine rolle, weil ein Client kann ja neugestartet werden, fordert dann die letzten Daten vom Server an und dann sollten da auch die Zeit stehen, wann sich diese Daten zu letzt geändert haben.

Die genauen Angaben wie Ort, Name und der gleichen, werden ja nur auf "User Interface" Ebene gebraucht. D.h. wenn ich ein Button auf einer meiner LCD Platinen drücke oder so und auch im "Web Interface". Im Prinzip könnte die Daten auch so gespeichert werden(ohne gleich eine DB wie mySQL oder so verwenden zu müssen).

Somit kann man die Daten die gesendet werden auf ein Minimum reduzieren.
Dateianhänge
DataFormate.odf.pdf
(25.25 KiB) 88-mal heruntergeladen
MFG
Michael Springwald

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Protokoll für Clients....

Beitrag von Warf »

Ja den Begriff Invariante Datentypen kenne ich so auch nicht :mrgreen: aber als Kombination der Worte Invariant (Also verändert sich während der Laufzeit nicht) und Datentyp dachte ich man kommt schon drau, ausserdem finde ich es klingt schlauer ;)

Und wie gesagt, da sich der Ort und der ModulName ja nur selten ändern muss man diese nicht als String speichern sondern man könnte einfach dem Server den String senden und dieser sendet dann eine ID zurück, und dann halt ID's statt Strings zu verwenden, und wenn ein anderer Client die ID auflösen will kann er einfach eine Get Anfrage an den Server senden. Somit hat jeder Client seine eigene Datenbank mit ID-Namen mapping, und wenn Informationen fehlen kann die über den Server synchronisiert werden.

Dann würde das Protokoll ungefähr so aussehen:

Code: Alles auswählen

Client Connects: ModulName:"NAME" Ort:"ORT" RegisterProperty:{"Property1", "Property2" ...}
Server antwortet: ID_Zugewiesen:ID OrtID:OID PropertyIDs= {PID1, PID2, ...}
//Server und client speichern einen Eintrag in ihrem Client und Orts Dictionary mit ID=NAME und OID=ORT
Client sendet Info: Timestamp:NOW Ort:OID Modul:ID Property:PIDX Value:"VALUE"


um kurz die Syntax zu erklären: am Anfang steht wer die information sendet, danach folgt Name:Value mit Name wäre dann das was übertragen wird (also z.b. der name im record) und Value dann der wirklich übertragene wert. Geschweifte Klammern bedeuten arrays.

Somit weist der Server allen Properties eine ID zu, sowie allen Modulen und allen Orten. Damit musst du bis auf für die Value keine Informationen mehr als String speichern, welche in der Übertragung wie auch in der Verarbeitung recht viel Overhead erzeugen.

Man könnte sogar einen schritt weiter gehen und die Values in pascal typen übersetzen. Also z.B. die Unit als Enum zu definieren und alle typen die nicht als String übertragen werden müssen (z.B. °C, hpa, mA, V, etc.) in einem Record zu übertragen und nur falls es nicht möglich ist die Daten in einem primitiven Datentypen darzustellen, dann wirklich den string zu versenden. Das würde das ganze natürlich viel komplizierter machen, da du 2 Routinen bräuchtest, eine um die Primitiven daten zu lesen, und einmal um die Strings zu lesen, falls strings benötigt werden. Ob sich das wirklich lohnt wage ich zu bezweifeln (da die values meist eh nur 4-5 Zeichen bei dir brauchen wäre die Einsparung nicht so groß).


Aber bevor ich es vergesse, denk immer daran die Byteorder zu converten, beim Versenden in die Network byte order converten und beim lesen anders rum (http://lazarus-ccr.sourceforge.net/docs ... htonl.html), sonst wird dir der Arduino echt lustige Fehler werfen


Dein dokument habe ich mir noch nicht angesehen, ich habe diesen Post grade geschrieben als du deinen geschrieben hast, ich werde gleich auf dein PDF eingehen in einem neuen Post

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Protokoll für Clients....

Beitrag von Warf »

So ich habe mir dein PDF grade mal angesehen, und du hast das ja so ungefähr gemacht, also die Klienten haben ja schon ID's und die Properties auch. Ich würde nur noch die ID's dynamisch registrieren lassen, somit bist du flexibler, wenn neue Klienten hinzukommen kannst du die ID einfach vom server wählen lassen (z.B. die nächste Nummer nach der aktuell höchsten ID) und dein Server weiß auch ganz genau welche Properties die Klienten haben, und du kannst damit auch z.B. wenn der Client eine Property anmelden will die der Server nicht kennt (weil der Server eine ältere Version ist als der Client) einfach vom Server -1 als ID zurückgeben lassen, damit weiß der Client, der Server unterstützt das nicht. Somit kannst du vor allem nach und nach dein system erweitern ohne direkt den Sourcecode von allem auf einmal anpassen zu müssen.

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Protokoll für Clients....

Beitrag von pluto »

Und wie gesagt, da sich der Ort und der ModulName ja nur selten ändern muss man diese nicht als String speichern sondern man könnte einfach dem Server den String senden und dieser sendet dann eine ID zurück, und dann halt ID's statt Strings zu verwenden, und wenn ein anderer Client die ID auflösen will kann er einfach eine Get Anfrage an den Server senden. Somit hat jeder Client seine eigene Datenbank mit ID-Namen mapping, und wenn Informationen fehlen kann die über den Server synchronisiert werden.

Im Prinzip habe ich das sogar schon:
Der Server speichert eine Liste der Daten die Empfangen wurden. Wenn ein Client startet kann er eine Anfrage an den Server machen um alle Zuletzt gesendeten Daten zu erhalten.

Ich werde mal über deine Idee nachdenken. Somit ist der User nicht mehr für die ID verantwortlich sondern der Server wäre es.
Die ID spielt bereits jetzt eine wichtige Rolle z.b. im WebInterface oder wenn ich Daten auf ein LCD anzeigen möchte, geht das über die ID.

Somit weist der Server allen Properties eine ID zu, sowie allen Modulen und allen Orten. Damit musst du bis auf für die Value keine Informationen mehr als String speichern, welche in der Übertragung wie auch in der Verarbeitung recht viel Overhead erzeugen.

Das ist der Unterschied, du speicherst diese Informationen Separat. Praktisch eine Liste für die Orte, eine Liste für die ID'S und soweiter.

Man könnte sogar einen schritt weiter gehen und die Values in pascal typen übersetzen. Also z.B. die Unit als Enum zu definieren und alle typen die nicht als String übertragen werden müssen (z.B. °C, hpa, mA, V, etc.) in einem Record zu übertragen und nur falls es nicht möglich ist die Daten in einem primitiven Datentypen

Stimmt. Wäre noch Sinnvoller. Nur Enum Werte können schlecht erweitert werden, wenn neue Datentypen hinzu kommen.

Aber bevor ich es vergesse, denk immer daran die Byteorder zu converten, beim Versenden in die Network byte order converten und beim lesen anders rum (http://lazarus-ccr.sourceforge.net/docs ... htonl.html), sonst wird dir der Arduino echt lustige Fehler werfen

Die Arduinos Arbeiten eigentlich recht zuverlässig. Ich nutzte Synapse im ganzen Projekt und für tcp nutzte ich lnet(da es einfach einfacher ist lnet hierfür zu nutzen).

Ich würde nur noch die ID's dynamisch registrieren lassen, somit bist du flexibler, wenn neue

Du meinst jede Info die ich Sende, soll der Server Verwalten? Ich würde das in den Händen der Client Entwickler lassen. So kann man diese ID'S besser verwenden.
Klar kann ich das auch umdrehen und sagen: Server gibt mir für diese Info eine ID.

Jeder Client kann ja mehrer Informationen Senden wie du in der PDF Datei gut sehen kannst.

und dein Server weiß auch ganz genau welche Properties die Klienten haben, und du kannst damit auch z.B. wenn der Client eine Property anmelden will die der Server nicht kennt (weil der Server eine ältere Version ist als der Client) einfach vom Server -1 als ID zurückgeben lassen, damit weiß der Client, der Server unterstützt das nicht. Somit kannst du vor allem nach und nach dein system erweitern ohne direkt den Sourcecode von allem auf einmal anpassen zu müssen.

Der Server macht mit den meisten Infos nicht, außer mit den Modul Informationen. Aber das wäre natürlich auch noch ein Wichtiger Punkt, für die spätere Entwicklung, wenn sich die Clients ändern und neue Funktionen hinzukommen oder alte geändert werden, so wie jetzt.

Ich werde mal ein paar Tage darüber nachdenken und mal sehen wie ich es einbaue, ich werde deine Idee mit den Records mal ausprobieren und auch den Ansatz,
das der Server jede Information eine ID geben muss.
MFG
Michael Springwald

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Protokoll für Clients....

Beitrag von pluto »

Ich habe mal deine Idee aufgegriffen und habe einen Server und einen Client erstellt. Das Senden und empfangen von einem Record klappt auch schon ganz gut, aber nur wenn ich Feste Größen werden.

Code: Alles auswählen

 
  TTransmitMessage = record
    Feld1:string[20];
    Feld2:string[20];
    ContentLength: UintPtr;
  end;
 
  TMessage = record
    Head: TTransmitMessage;
    Content: String;
  end;     
 
var
  Message:TMessage;
begin
  Message.Head.Feld1:='Test01';
  Message.Head.Feld2:='TestABC';
  Message.Content:='Dies ist ein test';
  Message.Head.ContentLength:=Length(Message.Content);
  PLClient.FCon.Send(Message.Head,Sizeof(Message.Head));
//  Nun ist mein Problem, ich weiß nicht wie ich den String am besten Sende ohne SendString zu nutzen.
  PLClient.FCon.Send(Pchar(Message.Content),SizeOf(String));
 
MFG
Michael Springwald

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Protokoll für Clients....

Beitrag von Warf »

Ich kenne mich mit Synapse nicht aus (entweder nutze ich Sockets unit oder Indy) aber von der syntax die du verwendest denke ich wäre das schreiben etwa so etwas:

Code: Alles auswählen

PLClient.FCon.Send(Message.Content[1], Message.Head.ContentLength); // Schreibe eine anzahl ContentLength Characters von dem String, beginnend mit dem ersten Char (Index 1)


Wenn du mit RangeChecks Debuggst wird, wenn der String leer ist, der Debugger bei dieser Zeile meckern (Da Index 1 nicht vorhanden ist, aber der tatsächliche Zugriff darauf eh nicht stattfindet ist es egal). Dann kannst du stattdessen einfach

Code: Alles auswählen

PLClient.FCon.Send(PChar(Message.Content)^, Message.Head.ContentLength); 

verwenden. PChar(Message.Content) ist der Zeiger auf den ersten Charakter des Char arrays (aus dem ja der String besteht)

Das lesen wäre dann wohl so etwas wie (Komplett geraten, ich kenne die Synapse funktionen überhaupt nicht):

Code: Alles auswählen

PLClient.FCon.Read(Message.Head, SizeOf(Message.Head));
SetLength(Message.Content, Message.Head.ContentLength); // Wichtig vor dem lesen den speicher alloziieren
PLClient.FCon.Read(PChar(Message.Content)^, Message.Head.ContentLength);

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Protokoll für Clients....

Beitrag von pluto »

Ich kenne mich mit Synapse nicht aus

Ich nutzte hierfür LNET. Synapse nutzte ich an andere Stelle.

Gut, deinem Beispiel entnehme ich, ich kann auch bei SendString bleiben. Die Methode macht es intern ganz ähnlich... Dann haben Record für mich keinen Sinn.
MFG
Michael Springwald

Antworten