Guten Morgen,
ich bin gerade dabei das erste mal TcpServerUtils einzusetzen um einen TCP-Server aufzubauen und brauche mal etwas Hilfe bei der Verwendung.
Ich habe in anderen Modulen, die TcpServerUtils einsetzen in der ReadFn immer ein ähnliches Konstrukt gefunden. Hier das Beispiel aus MQTT2_SERVER_Read:
my ($hash, $reread) = @_;
if($hash->{SERVERSOCKET}) { # Accept and create a child
my $nhash = TcpServer_Accept($hash, "Log2Syslog");
return if(!$nhash);
$nhash->{CD}->blocking(0);
readingsSingleUpdate($hash, "nrclients",
ReadingsVal($hash->{NAME}, "nrclients", 0)+1, 1);
return;
}
my $sname = $hash->{SNAME};
my $cname = $hash->{NAME};
my $c = $hash->{CD};
........
Meine zwei grundsätzliche Verständnisprobleme sind:
1. Wenn in der ReadFn immer in den if($hash->{SERVERSOCKET})-Zweig abgebogen wird (mit einem return darin), wie werden dann jemals Daten durch den darunter liegenden Code gelesen, da ja $hash->{SERVERSOCKET} immer existiert solange der Server offen ist ?
2. TcpServer_Accept liefert ja eine Hashreferenz zurück ($nhash) die "{CD}" enthält. Im Code unter der If-Bedingung wird "my $c = $hash->{CD}" verwendet. Ich habe aber bisher nirgendwo eine Stelle gefunden wo aus $nhash->{CD} $hash->{CD} wird.
Ich habe es bis jetzt zwar hinbekommen Daten mit dem Server zu empfangen, aber nur wenn ich ein solches Konstrukt in der ReadFn verwende:
if($hash->{SERVERSOCKET}) { # Accept and create a child
my $nhash = TcpServer_Accept($hash, "Log2Syslog");
return if(!$nhash);
$nhash->{CD}->blocking(0);
my $client = $nhash->{NAME};
$hash->{TCPPADDR} = (split '_', $client)[-2];
my $c = $nhash->{CD};
my $ret = sysread($c, $data, 1024); # returns undef on error, 0 at end of file and Integer, number of bytes read on success.
.....
D.h. es klappt wenn ich das Datenlesen innerhalb des if($hash->{SERVERSOCKET})-Zweiges mache unter Verwendung von $nhash->{CD}.
Irgendetwas übersehe ich was mich daran hindert TcpServerUtils so einzusetzen wie es andere Module vormachen.
Für ein paar Erläuterungen wäre ich euch sehr dankbar.
LG und einen schönen Sonntag,
Heiko
if( $hash->{SERVERSOCKET} ) { # Accept and create a child
TcpServer_Accept( $hash, $hash->{'NAME'} );
return;
}
# Read 1024 byte of data
my $buf;
my $ret = sysread($hash->{CD}, $buf, 2048);
# When there is an error in connection return
if( !defined($ret ) or $ret <= 0 ) {
CommandDelete( undef, $name );
Log3 $name, 5, " ($name) - Connection closed for $name";
return;
}
...
Bei mir geht es so ohne Probleme. Das Lesen erfolgt also außerhalb der Bedingung. Ganz zum Schluss wird dann $buf an eine bearbeitende Funktion weiter gegeben.
Morgen Leon,
ZitatBei mir geht es so ohne Probleme....
Ja, ich weiß :) Habe auch im AMAD-Modul nachgeschaut.
Kann nur nicht erkennen _wie_ bzw. warum es so z.B. auch bei dir funktioniert. Das ist ja gerade mein Hirnknoten....
Guten Morgen Heiko,
Eventuell liegt es am Open. So tief stecke ich da auch nicht drin. Damals hat mir Andre geholfen, ist schon ne ganze Weile her.
Zeig mal bitte Deine Open.
Open ist ganz unspektakulär.
$lh = "global" if(!$lh);
my $ret = TcpServer_Open($hash,$port,$lh);
if($ret) {
$err = "Can't open Syslog TCP Collector at $port: $ret";
Log3 ($hash, 1, "Log2Syslog $name - $err");
readingsSingleUpdate ($hash, 'state', $err, 1);
return;
}
ZitatWenn in der ReadFn immer in den if($hash->{SERVERSOCKET})-Zweig abgebogen wird (mit einem return darin), wie werden dann jemals Daten durch den darunter liegenden Code gelesen, da ja $hash->{SERVERSOCKET} immer existiert solange der Server offen ist ?
Die einzige Aufgabe der Instanz mit SERVERSOCKET ist TcpServer_Accept durchzufuehren (und evtl. noch Statistiken). Durch den Accept wird eine weitere Instanz des gleichen Typs angelegt die eine Verbindung repraesentiert und im ReadFn die eigentliche Arbeit macht:
- ohne SERVERSOCKET dafuer mit CD/FD, PEER und PORT. CD/FD enthaelt den neuen Filedeskriptor.
- mit TEMPORARY (damit es nicht gespeichert wird)
- SNAME verweist auf die "richtige" Instanz, damit man die Attribute abfragen kann.
ZitatIch habe aber bisher nirgendwo eine Stelle gefunden wo aus $nhash->{CD} $hash->{CD} wird.
Passiert indirekt: TcpServer_Accept traegt den neuen Filedeskriptor in die globale %selectlist ein, damit wird ReadFn von fhem.pl/select mit dem temporaeren Instanzhash aufgerufen, wenn Daten genau bei dieser Verbindung anstehen.
Siehe auch "list TYPE=FHEMWEB", bzw. "man -s2 accept",
Guten Morgen Rudi,
Vielen Dank für die gute Erklärung. Dann muss Heiko seine Funktion also auf jeden Fall auch außerhalb der Bedingung weiter funktionieren.
Grüße
Morgen Rudi,
danke für die Erläuterung der Zusammenhänge. Wie Cooltux schon schrieb, müsste es ja auch wie bei euch auch funktionieren.
Ich versuche es jetzt mit den neuen Erkenntnissen nochmal.
Versuch es mal in einem minimalistischen Modul. Nur die TcpServer Geschichten einbauen. Habe ich auch gemacht, aber nur um ein Netzwerkgeräte zu emulieren bei der Entwicklung eines Modules.
Wollte nur noch mal kurz Rückmeldung geben, dass der TCP-Server nun so läuft wie ich es erwartet habe. Dank der Info von Rudi bezüglich der Zusammenhänge konnte ich eine Reihe von Fehlern beseitigen die sich vor allem aus dem Switchen des Hash-Kontexts bei dieser Verwendung ergeben haben.
Jetzt gehts an die Feinheiten mit den vllt. nächsten Hürden ;)
Vielen Dank euch !
LG,
Heiko