Server Socket, Cutter,

Moin,

ich habe ein Problem mit einem Server Socket, an den mehrere ESP Daten im JSON Format senden, das Paket hat ein ordentliches Endekennzeichen <ETX>.

Der ESP liefert Daten, die größer als 1460 Bytes Nutzdaten sind, dadurch wird das Paket netzwerk-bedingt in mehrere Teile zerlegt.

Der Server Socket bekommt somit 2 Teile, die ich über den Cutter mit dem <ETX> als Trennung dann an eine Registervariable weitergebe.

Leider kommt es vor, dass zwei ESP gleichzeitig mit dem Server Socket kommunizieren und ich somit ein vermischtes und somit defektes Paket erhalte.

Wieso wird nicht erst eine eingehende Verbindung bis zum Ende (closing connection) komplett abgearbeitet und dann erst die nächste?

Wie kann ich das Mischen verhindern?

Wer garantiert dir dass Verbindungen geschlossen werden müssen?
Es gibt durchaus Anwendungen wo es gewollt ist das die Verbindung offen bleibt.

Der ServerSocket kennt zwar den Absender, kann aber, da er dein Protokoll nicht kennt, die Pakete nicht erst von Client A zusammenfügen und dann B weitergeben.
Der Cutter wiederum kann nur den Datenstrom verarbeiten, und kennt den Client nicht.

Einfachste Lösung wäre wohl zu sehen, dass deine Pakete kleiner werden.

Alternativ kannst du dir einen Splitter als PHP-Modul bauen, welches je nach Client sich nur auf diese Daten Registriert; ist aber eher aufwändig.
Im internen Datenaustausch gibt der ServerSocket den Client von welchen die Daten kommen nämlich mit.

Damit beschäftigt habe ich mich auch noch nicht, muss ich aber demnächst. Eventuell ist da ja dann so ein Splitter als Nebeneffekt entstanden :wink:
Michael

Moin,
wäre nicht ein möglicher Workaround einfach pro Client einen Server-Socket aufzusetzen? Dann ist immer klar von welchem Client die Daten kommen. Natürlich nur wenn die Anzahl der Clients überschaubar ist…

Das geht natürlich auch, einfach pro Gerät einen Port.
Ist aber auch irgendwie unschön…
Michael

Deshalb heißt es „workaround“ - „geht, aber nicht schön“. :wink:

Wie schon gesagt ist der ServerSocket eher für „persistente“ und offene Verbindungen gedacht. Für deinen Fall kannst du entweder mehrere ServerSockets verwenden (einfach) oder einen ServerSocket verwenden und in der RegisterVariable über $_IPS[‚CLIENTIP‘] und $_IPS[‚CLIENTPORT‘] die Daten korrekt zusammenfassen. Dazu kannst du je ClientIP mit RegVar_SetBuffer / RegVar_GetBuffer die Daten zwischenspeichern. Dies ist ab IP-Symon 4.1 möglich.

paresy

PS: Wir fügen das mal in die Doku… Irgendwie fehlt das…

Ahhh… Das mit der RegVar war mir ja ganz neu.
Cool dass das dann auch mit Scripten funktioniert.
Michael

Ja :smiley: Hatte ich auch ganz vergessen. Musste auch erstmal nachgucken, weil es nicht in der Doku war :slight_smile:

paresy

Gibt es denn auch Befehle vom ServerSocket zum gezielten Antworten ? Bzw. fehlen die auch in der Doku?
Michael

Ok, ich schaue mir das mal genauer an.

Eigene Sockets funktioniert nicht, da das Ziel IP:Port fest vorgegeben ist.

ok, grundsätzlich verstanden, aber ich habe dann in etwa folgende Struktur:

I/O --> Register Variable 1 --> Script 1 --> Register Variable 2 --> Script 2

Script 1: eigene Aufbereitung der Pakete, also Zwischenspeicherung, Zusammensetzen und Weiterleiten
Script 2: Verarbeitung des Gesamtpaketes, Plausi-Check, Aufbereitung, Variablen setzen

Da Script 1 ja bei jedem Einzelpaket aufgerufen wird muss ich die Daten jeweils pro Gerät/IP zwischenspeichern.

Wie speichere ich die am sinnvollsten?
Für jede Quell-IP eine eigene Variable oder als serialisiertes Array?

Da kommt bei vielen Geräten dann einiges zusammen :eek:.

Ich habe da eine Idee… dauert aber ein paar Stunden :wink:
Michael

Würde dir das hier helfen ?
Du hättest dann pro Gerät die Möglichkeit einen Cutter + RegVar anzulegen.
Damit werden die Daten pro Client gesammelt und nicht mehr vermischt.
Dennoch reicht ein Auswerte-Script, da $_IPS[‚INSTANCE‘] die InstanzID der RegVar enthält.
Der ‚Client Splitter‘ ist ein PHP-Modul, welches ich aktuell eh bauen mußte um ein IPS ‚Problem‘ zu umgehen :smiley:


Michael

Den Ansatz würde ich mir gern mal ansehen, ich wollte eh’ „irgendwann“ ein Modul daraus machen.

Cutter und RegVar wären eigentlich unnötig, wenn ich die Verarbeitung direkt im Modul machen könnte.

Grundsätzlich senden an eine IP:Port „beliebig viele“ Geräte, die ich nicht manuell anlegen möchte, aber die könnte das Modul ja selber als Instanzen anlegen.
Und dazu gibt es dann „beliebig viele“ Werte, die ich in Variablen ablegen möchte.

Eigentlich habe ich alles im JSON, aber durch die TCP/IP Problematik zerlegt in 1460 Bytes Nutzdaten Pakete, die wieder zusammengeführt werden müssen.

So ähnlich müsstest du das Im Squeeze Modul doch auch haben, dann können ja auch „einfach“ Geräte dazu kommen.

Ich Antworte mal in Teilen :slight_smile:

Ja das geht, dann brauchst du auch den Client-Splitte nicht, da der ServerSocket ja die ClientIP mitliefert und du die Daten im Modul sauber zusammenbauen kannst.

Ganz böse. Nicht machen.
Es gibt nicht ein Modul (außer vielleicht das SymconHue, und dort ist das auch Böse) welches automatisch eigene Instanzen anlegt.
Was aber geht, ist ein Konfigurator, welcher z.B. aber auf Anforderung des User das Netzwerk scannt und dann bei Bedarf Instanzen erzeugt.

Das ist ziemlich simpel im Modul zu umgehen :slight_smile:
Einen Empfangsfilter setzen auf die ClientIP mit

$this->SetReceiveDataFilter('.*"ClientIP":"' . $this->ReadPropertyString('ClientIP') . '".*');

Dann bekommst du in dieser Instanz nur die Daten zu einem Gerät.
Und die dann so lange sammeln bis das Endzeichen kommt.
Ich sende dir mal eine PN mit dem Splitter zum testen, dort siehst du es dann mit den Filter und kannst diesen Code als Basis nutzen.

Das ist ein ClientSocket und nur ein Gerät (der LMS).
Die SqueezeBoxen senden ja nichts direkt an IPS. Und der LMS macht das schon alles richtig.
Hier ist nur irgendwann die Datenflut zu groß, aber da bin ich am umbauen.
Und einen Konfigurator bekommt das Modul auch noch.
Der ließt dann den Server aus, und zeigt dir dann alle Player und Instanzen tabellarisch an. So wie z.B. der Homematic-Konfigurator :slight_smile:

Michael

Danke für den Input, schaue ich mir nachher mal an.

Die Geräte senden halt unregelmäßig ihre Daten selber, da kann ich auch nicht „das Netz scannen“.

Der Anwender kann ein neues Gerät mit Ziel-IP:Port konfigurieren und dann sendet das an den ServerSocket.

Alles andere danach müsste im IPS eigentlich automatisch erfolgen.

Im Normalfall wissen die Splitter-Instanzen nicht welche Device-Instanzen schon in IPS existieren und welche nicht.
Die Daten werden einfach verteilt, egal ob da jemand ist der sie annimmt.
Somit wird das mit dem automatischen anlegen auch nicht funktionieren.
Außer du willst dann jedesmal wenn Daten eintreffen noch die Instanzliste durchforsten und die Konfig abgleichen, was natürlich Rechenleistung und PHP-Slots kostet. So entsteht dann nachher die Lags im Datenaustausch.
Darum sagte ich, nicht machen, ganz böse :wink:
Dann muss der User halt die Instanz für ein Gerät per Hand anlegen und die IP eintragen.
Michael

Naja, beim Schreiben der Variablen weiß ich das auch nicht, deshalb setze ich den Wert mit

function CreateVariableByIdent($id, $ident, $name, $type)
{
	$vid = @IPS_GetObjectIDByIdent($ident, $id);
	if($vid === false) {
		$vid = IPS_CreateVariable($type);
		IPS_SetParent($vid, $id);
		IPS_SetName($vid, $name);
		IPS_SetIdent($vid, $ident);
	}
	return $vid;
}

und da ich bei der Dummy Instanz darüber auch nicht weiß, ob es die schon gibt, gibt es etwas vergleichbares zur Dummy Instanz.

Theoretisch könnte der Anwender die IP auch ändern, bekannt ist die MAC vom ESP, die ich überall als Teil des Ident verwende.

Variablen sind aber etwas anderes als Instanzen.

Diese Funktion brauchst du in den Modulen für Variablen auch gar nicht mehr.
Das bringt das SDK alles fertig mit.
Da gibt es:


MaintainVariable($Ident, $Name, $Type, $Profile, $Position, $Keep)
RegisterVariableBoolean($Ident, $Name, $Profile, $Position) // auch Integer, String, Float

Sollte man auch nutzen, sonst gibt es keine Garantie, dass es nach einem Update von IPS noch funktioniert :wink:

Michael