[gelöst] Probleme mit UDP Device

Begonnen von marcus42, 27 November 2015, 21:18:20

Vorheriges Thema - Nächstes Thema

marcus42

Hallo zusammen,

ich versuche mich gerade an meinem ersten Modul für ein Device, welches ich per UDP steuern kann.
Dabei stosse ich auf Probleme, die ich alleine nicht in den Griff bekomme, da meine Perlkenntnisse noch ausbaufähig sind ;-)

Bitte habt etwas Nachsicht mit mir.

Das Device sendet zum einen Broadcasts auf einem bestimmten Port um Statusänderungen ohne Polling verfügbar zu machen.
Zum anderen kann ich auch gezielt Befehle per UDP absetzen und damit den Status abfragen sowie Steuerkommandos absetzen.

Meine Probleme liegen darin, dass ich die Broadcast noch nicht auslesen kann.

Jetzt mal konkret am Code:

Das Define ruft eine Connect Methode auf:


sub KEBA_Define($$) {

    my ($hash, $def) = @_;
    my @param = split('[ \t]+', $def);
   
    if(int(@param) != 4) {
return "KEBA_Define: number of arguments incorrect. Usage:\n" .
         "define <name> KEBA <host> <port>";
    }

    $hash->{Host}  = $param[2];
    $hash->{Port} = $param[3];

Log3 $hash, 3, "$hash->{NAME} will read from KEBA at $hash->{Host}:$hash->{Port} " ;

    KEBA_connect($hash);
}


In der Connect-Methode gebe ich peerAdress und peerPort an.


sub KEBA_connect($){

    my ($hash) = @_;

    my $name = $hash->{NAME};
    my $ip = $hash->{Host};
    my $port = $hash->{Port};

    my $socket = IO::Socket::INET->new(
          Proto    => 'udp',
  LocalPort => $port,
          PeerPort => $port,
          PeerAddr => $ip
      );

if($socket) {

$hash->{STATE} = "Connected";
    $hash->{LAST_CONNECT} = FmtDateTime( gettimeofday() );

    $hash->{FD}    = $socket->fileno();
    $hash->{CD}    = $socket;         # sysread / close won't work on fileno
    $hash->{CONNECTS}++;
    $selectlist{$name} = $hash;

    Log3 $name, 3, "$name: connected to $hash->{Host}";
}
}




In der Read-Methode werte ich die Daten an der Netzwerkschnittstelle dann aus:



sub KEBA_Read($){
 
  my ($hash) = @_;
  my $socket = $hash->{CD};
  my $response;

  $socket->recv($response,512);

  Log 4, "Daten: $response";

  .... etc


Das Problem besteht nun darin, dass die Readmethode von den Broadcasts nichts mitbekommt, sondern nur auf die von mir abgesetzten Befehle reagiert. Hierbei liegen dann Daten an der Schnittstelle an.

Auf der Konsole habe ich ein kleines Standalone-Perl-Skript geschrieben, welches wie gewünscht auf die Broadcasts reagiert:


...

my ($sock);
my $data;

$sock=IO::Socket::INET->new(
  Proto => 'udp',
  LocalPort => 7090
) or die "Can't bind: $@\n";

print scalar localtime().": Awaiting data...\n";

while($sock->recv($data, 512)) {

  print scalar localtime().": Rcvd $data"; #from $peerhost $peerip\n";

....


Wie man sieht habe ich für den Broadcast nur den LocalPort angegeben. Das klappt auch prima.

Meine Frage ist nun:

Wie schaffe ich es Broadcasts und Direkte Kommunikation in meinem Modul unter einen Hut zu bringen?
Muss ich hierfür 2 Sockets verwalten? Wenn ja, wie muss ich diese im Modul genau handhaben?

Vielleicht hat ja von Euch jemand eine Idee ...

Über eine Rückmeldung würde ich mich jedenfalls freuen :-)

VG
Marcus

Sidey

Mit welchem Port definierst Du das device?

Damit auf Port 7090 was empfängst, musst Du den auch als localport abgeben.

Du kannst auch mit netstat auf der Kommandozeile vom OS prüfen,  ob auf dem Port Anfragen entgegen genommen werden.
So wie Du das jetzt gemacht hast, ist eher eine virtuelle Verbindung etabliert.
Unter Umständen kann es notwendig sein, zwei Verbindungen zu etaböieren.

Hoffe Du komnst damit weiter
Signalduino, Homematic, Raspberry Pi, Mysensors, MQTT, Alexa, Docker, AlexaFhem,zigbee2mqtt

Maintainer von: SIGNALduino, fhem-docker, alexa-fhem-docker, fhempy-docker

marcus42

#2
PeerPort und LocalPort stehen beide auf 7090.

Mit PeerPort und PeerAdress bekomme ich aber die Broadcasts nicht mit.

Wie müsste ich denn die 2. Verbindung (z.B. im Hash) handhaben? 
Habe ich dann weiterhin nur eine Read-Methode? Wie gehe ich darin mit den beiden Verbindungen um?

Sidey

#3
Zitat von: marcus42 am 27 November 2015, 21:34:55
Mit PeerPort und PeerAdress bekomme ich aber die Broadcasts nicht mit.
Genau. Ein Broadcast ist ja nicht an deine Adresse gerichtet.
Mit diesen Einstellungen öffnest Du eine Verbindung zwischen deiner Adresse und der Adresse des Zielsystems:
Zitat
          my $socket = IO::Socket::INET->new(
          Proto    => 'udp',
          LocalPort => $port,
          PeerPort => $port,
          PeerAddr => $ip
          );

Du solltest ohne PeerPort und ohne PeerAddr die Verbindung initialisieren, wenn Du auf Broadcasts reagieren möchtest.
Wie Du in FHEM nun im detail auf zwei Verbindung in einem Modul reagierst, das weiss ich jetzt leider nicht.
Einfach wäre, wenn Du zwei Module erstellst. Aber sicher klappt es auch in einem Modul zwei sockets zu verwalten.
Du müsstest vermutlich nur den fhem code etwas nachverfolgen um zu verstehen wo Du eine 2. read Methode einbinden musst.

Grüße Sidey

Signalduino, Homematic, Raspberry Pi, Mysensors, MQTT, Alexa, Docker, AlexaFhem,zigbee2mqtt

Maintainer von: SIGNALduino, fhem-docker, alexa-fhem-docker, fhempy-docker

marcus42

Danke für Deine schnelle Rückmeldung! Ich werde dann morgen nochmal etwas weitertüfteln.

justme1968

ob das ganze mit einem oder zwei bzw. mehreren sockets einfacher ist hängt von der restlichen programm logik ab und davon ob beide dienste eventuell auf dem gleichen rechner laufen. dann kannst du den gleichen port nicht zum senden und empfangen auf machen.

wenn du mit einem einzigen socket arbeiten willst darfst du peer daten beim socket erzeugen nicht mit angeben sondern musst sie jeweils beim send mit angeben. dann kannst du das eine udp socket verwenden um die broadcasts zu empfangen und gezielt an einen host etwas zu senden.

wenn du magst kannst du ja mal in das plex modul hier: http://forum.fhem.de/index.php/topic,43052.msg353477.html#msg353477 schauen. da verwende ich diverse udp broadcast, udp multicast und tcp sockets zum senden und empfangen inklusive herausfiltern der eigenen broadcast nachrichten.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

marcus42

Danke André für Deine Antwort und den Link zum PlexModul! Das werde ich mir morgen mal genauer ansehen. VG Marcus

marcus42

#7
Hallo Andre,

ich erstelle den socket in der Connect-Methode nun so:


my $socket = IO::Socket::INET->new(Proto    => 'udp', LocalPort => 7090);


Die Send-Methode habe ich passend angepasst:


my $ipaddr = inet_aton('192.168.2.43');
my $destaddr = sockaddr_in(7090, $ipaddr);
send($socket, $command, 0, $destaddr);


Das Senden von Befehlen klappt damit prima. Die Antwort erhalte ich per Read-Methode.
Aber die Read Methode reagiert leider immer noch nicht auf die Broadcasts.

netstat sagt dazu:



netstat -p udp

Active Internet connections
Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)   
udp4       0      0  *.7090                 *.*                               


Muss ich evtl. noch weitere Parameter setzen?

VG
Marcus

marcus42

Hallo Andre,

Entwarnung! Es klappt nun tatsächlich.

Ich hatte fälschlich angenommen, dass bereits Broadcasts versendet wurden.
Mein Device hatte bisher aber noch keinen versendet.

Mit einmal Aus- und wieder Einschalten habe es nun für einige Broadcasts gesorgt :-)

Und siehe da, sie tauchen im fhem-Logfile auf...

Danke für Eure Tipps!

VG
Marcus