FHEM Forum

FHEM - Entwicklung => FHEM Development => Thema gestartet von: DS_Starter am 16 September 2018, 07:47:30

Titel: Frage zu TcpServerUtils und Einbindung in ReadFn
Beitrag von: DS_Starter am 16 September 2018, 07:47:30
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
Titel: Antw:Frage zu TcpServerUtils und Einbindung in ReadFn
Beitrag von: CoolTux am 16 September 2018, 08:01:24

    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.
Titel: Antw:Frage zu TcpServerUtils und Einbindung in ReadFn
Beitrag von: DS_Starter am 16 September 2018, 08:08:14
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....
Titel: Antw:Frage zu TcpServerUtils und Einbindung in ReadFn
Beitrag von: CoolTux am 16 September 2018, 08:13:14
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.
Titel: Antw:Frage zu TcpServerUtils und Einbindung in ReadFn
Beitrag von: DS_Starter am 16 September 2018, 08:22:46
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;
      }
Titel: Antw:Frage zu TcpServerUtils und Einbindung in ReadFn
Beitrag von: rudolfkoenig am 16 September 2018, 08:23:50
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",
Titel: Antw:Frage zu TcpServerUtils und Einbindung in ReadFn
Beitrag von: CoolTux am 16 September 2018, 08:31:22
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
Titel: Antw:Frage zu TcpServerUtils und Einbindung in ReadFn
Beitrag von: DS_Starter am 16 September 2018, 08:38:03
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.
Titel: Antw:Frage zu TcpServerUtils und Einbindung in ReadFn
Beitrag von: CoolTux am 16 September 2018, 09:41:31
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.
Titel: Antw:Frage zu TcpServerUtils und Einbindung in ReadFn
Beitrag von: DS_Starter am 16 September 2018, 18:00:24
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