Detektion Abbruch TCP-Verbindung

Begonnen von vbs, 05 Mai 2014, 19:19:05

Vorheriges Thema - Nächstes Thema

vbs

Hi Ihr,

ich hab eine Frage, die sich auf diesen Thread bezieht:
http://forum.fhem.de/index.php?topic=21022.0

Konkret habe ich das Problem, dass das Modul XBMC nicht mitbekommt, wenn mein XBMC sich schlafen legt, da XBMC die TCP-Verbindung vorher nicht schließt. Wenn ich es richtig sehe, dann fragt fhem brav per select den file descriptor ab, ob Daten anliegen, was natürlich nie der Fall ist. Fhem merkt aber nicht, dass die Gegenstelle gar nicht mehr da ist.


Wenn ich es richtig verstehe, dann wurde ja genau für diesen Zweck die Funktion DevIo_Expect eingebaut, mit der ein Modul Daten schickt und eine Antwort erwartet. Ich verstehe aber nicht so recht, WO ich diese Funktion innerhalb des XBMC-Moduls aufrufen soll. Die ready- bzw. read-Funktion wird ja nie angesprungen, richtig? Meine einzige Idee wäre, dass ich einen InternalTimer definiere, der irgendwie alle 120 Sekunden aufgerufen wird und darin das DevIo_Expect aufruft.
Ist das so gedacht oder gibt es da einen besseren Weg?

Außerdem: Ich muss ja in dem DevIo_Expect dem Gerät Daten schicken. Die JSON-API von XBMC bietet glücklicherweise einen ping-Befehl an: http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#JSONRPC.Ping
Der würde sich da anbieten zum Aufrufen in DevIo_Expect, oder?

rudolfkoenig

Seit eine Weile setzt DevIo_OpenDev SO_KEEPALIVE, also sollte FHEM nach ca 2 Stunden (ueblicher default im OS) mitkriegen, dass da was nicht stimmt. Wenn man es schneller mitkriegen will, dann muss man das selbst machen.

vbs

Ok, danke. Ich hab hier mal etwas gebastelt für XBMC: http://forum.fhem.de/index.php/topic,10075.msg166105.html#msg166105

Sieht jetzt im Prinzip so aus (das XBMC_QueueCheckConnection ist der springende Punkt):
sub XBMC_Init($)
{
  my ($hash) = @_;
 
  #since we just successfully connected to XBMC I guess its safe to assume the device is awake
  readingsSingleUpdate($hash,"system","wake",1);
 
  XBMC_Update($hash);
 
  XBMC_QueueCheckConnection($hash);
 
  return undef;
}

sub XBMC_QueueCheckConnection($) {
  my ($hash) = @_;
 
  # AFAIK when using http this module is not using a persistent TCP connection
  if($hash->{Protocol} ne 'http') {
    InternalTimer(time() + 60, "XBMC_CheckConnection", $hash, 0);
  }
}

sub XBMC_CheckConnection($) {
  my ($hash) = @_;

  my $obj = {
    "method" => "JSONRPC.Ping",
  };
  $obj->{id} = XBMC_CreateId();
  $obj->{jsonrpc} = "2.0"; #JSON RPC version has to be passed

  # remember: we only get here when using TCP (not HTTP)
  my $json = encode_json($obj);
  my $answer = DevIo_Expect($hash, $json, 3.0);
  if(defined($answer)) {
    #xbmc is alive. so keep bugging it
    XBMC_QueueCheckConnection($hash);
  }
}

vbs

Also das klappt ja alles soweit gut, aber kann es sein, dass die Sache einen Haken hat? In dem Moment, in dem das Gerät nicht mehr erreichbar bis, läuft doch fhem einmalig in den Read-Timeout (default 3 Sekunden) rein, oder? Also wäre dann fhem, wenn das Gerät ausgeschaltet wird, für 3 Sekunden blockiert, oder irre ich mich da?

Wenn ja, gibts da Ideen, was man dagegen machen kann? Den Check im Hintergrund aus einem geforkten Prozess zu machen, ist wahrscheinlich schwierig, da man dann ja parallel auf dem schon bestehenden socket lesen/schreiben müsste. Die einzige Idee, die ich im Moment hätte, wäre, dass man statt Daten auf dem bestehenden Socket zu schreiben, in einem geforkten Prozess eine zweite TCP-Verbindung aufmacht und guckt, ob das erfolgreich ist.

rudolfkoenig

Wenn dich die 3 Sekunden stoeren, dann kein DevIo_SimpleReadWithTimeout / DevIo_Expect verwenden, sondern vom globalen select per ReadFn benachrichtigen, und einen InternalTimer setzen.

Ist mAn einfacher als die Geschichte mit dem fork()
Oder HttpUtils_NonblockingGet verwenden.

vbs

Ok, verstehe. Nicht dass diese 3 Sekunden super schlimm sind, aber ich werde trotzdem versuchen das noch zu optimieren, danke!

Eine Frage zu dem BlockingCall:
Das Wiki sagt, man solle den Rückgabewert aktuell laufender Calls unterhalb von {helper} speichern:
Die Funktion BlockingCall() gibt als return-Wert einen Hash mit mehreren Informationen zurück. Dieser Ergebnis-Hash ist in mehrerer Hinsicht sehr wichtig für die Benutzung in einem Modul, dazu in den folgenden Punkten mehr.
Eine gute Möglichkeit ist es dieses Ergebnis unterhalb von $hash->{helper} zu platzieren. So sieht es der Enduser nicht, kann aber bei Bedarf via list-Befehl angezeigt werden.


Zu helpers wiederum sagt das Wiki, dass diese gespeichert werden:
helper
Logische Rubrik: Hilfsvariable, Device Reading
Wird gespeichert

Das "wird gespeichert" versteh ich so, dass "helpers" im statefile gespeichert wird und nach einem Neustart von fhem wieder zur Verfügung steht.

Das würde doch bedeuten, dass, wenn ich fhem beende, während der BlockingCall läuft und dann fhem neu starte, dann ist helpers wieder gefüllt, aber der BlockingCall läuft eigentlich gar nicht. Das wiederum heisst, ich kann den BlockingCall auch nicht starten, da ja vorher (per Abfrage von helpers) geprüft werden soll, ob bereits ein BlockingCall läuft. Oder?

Markus Bloch

Nein, alle Daten unter $hash->{helpers} werden nirgendwo gespeichert, sondern verbleiben zur Laufzeit im RAM. Sie dienen dazu Daten zu halten, die der Nutzer nicht wirklich zu sehen brauch, die aber für das Modul notwendig sind.

Viele Grüße

Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

vbs

Ahh ok, danke, dann hab ich das wohl missverstanden...