Autor Thema: Broadcast und receive über UDP  (Gelesen 229 mal)

Offline Thorsten Pferdekaemper

  • Tester
  • Hero Member
  • ****
  • Beiträge: 3353
  • Finger weg von der fhem.cfg
Broadcast und receive über UDP
« am: 16 März 2017, 10:21:55 »
Hi,
zum "Discovery" für Roomba980 Staubsaugerroboter wird über UDP broadcast der Text "irobotmcs" geschickt, worauf die Sauger mit ein paar Informationen antworten. Ich habe versucht, das in Perl hinzuschreiben, aber wohl nicht ganz richtig:
sub discovery($){

    my $sock = new IO::Socket::INET(
            LocalPort => 5678,  # TODO: do we have to use this port?
    PeerPort => 5678,
                    Proto => 'udp',
    Timeout => 1,
    Broadcast => 1);   # TODO: error handling or die('Error opening socket.');
    my $data = "irobotmcs";
    print $sock $data;

my ($datagram,$flags);
    while (1) {
        $sock->recv($datagram,100,$flags);
        print "Received datagram from ", $sock->peerhost,
                  ", flags ", $flags || "none", ": $datagram";
    }
    $sock->close();
};
Ich weiß, dass das eine Endlosschleife ist und blockiert, aber das ist nicht der Punkt. Es scheint einfach gar nichts zu empfangen. Bevor ich jetzt anfange, großartig nachzuforschen, wollte ich fragen, ob das jemand vielleicht schon einmal gemacht hat und mir ein paar Zeilen Coding spendieren könnte.

Das ganze ist übrigens der Versuch, folgendes nodejs-Coding nach Perl zu bringen:

function discovery (cb, full) {
  const server = dgram.createSocket('udp4');

  server.on('error', (err) => {
    server.close();
    cb(err);
  });

  server.on('message', (msg) => {
    try {
      let parsedMsg = JSON.parse(msg);
      if (parsedMsg.hostname && parsedMsg.ip && parsedMsg.hostname.split('-')[0] === 'Roomba') {
        server.close();
        console.log('Robot found! with blid/username: ' + parsedMsg.hostname.split('-')[1]);
        console.log(parsedMsg);
        cb(null, full ? parsedMsg : parsedMsg.ip);
      }
    } catch (e) {}
  });

  server.on('listening', () => {
    console.log('Looking for robots...');
  });

  server.bind(5678, function () {
    const message = new Buffer('irobotmcs');
    server.setBroadcast(true);
    server.send(message, 0, message.length, 5678, '255.255.255.255');
  });
}

Danke&Gruß,
    Thorsten
RasPi
Heizkessel-Steuerung per Arduino und HTTPMOD
und einen Haufen Homematic (Wired)

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 16025
Antw:Broadcast und receive über UDP
« Antwort #1 am: 16 März 2017, 10:31:24 »
LocalPort braucht man, wenn man einen Server haben will, PeerPort fuer einen Client. Keine Ahnung was passiert, wenn man beide spezifiziert, mein Bauch sagt, lieber zwei IO::Socket::INET Instanzen verwenden.
Und ich wuerde parallel ein tcpdump oder Vergleichbares starten, um zu sehen, was passiert.

Offline Thorsten Pferdekaemper

  • Tester
  • Hero Member
  • ****
  • Beiträge: 3353
  • Finger weg von der fhem.cfg
Antw:Broadcast und receive über UDP
« Antwort #2 am: 16 März 2017, 10:51:06 »
Hi,
ok, das werde ich dann mal ausprobieren.
...aber: Das klingt nicht so, als ob Du das schonmal gemacht hast. Hat es irgendwer schon einmal gemacht?
Gruß,
   Thorsten
RasPi
Heizkessel-Steuerung per Arduino und HTTPMOD
und einen Haufen Homematic (Wired)

Offline Thorsten Pferdekaemper

  • Tester
  • Hero Member
  • ****
  • Beiträge: 3353
  • Finger weg von der fhem.cfg
Antw:Broadcast und receive über UDP
« Antwort #3 am: 17 März 2017, 09:59:15 »
So, teilweise Erfolg:
    my $sock = new IO::Socket::INET(
PeerPort => 5678,
PeerAddr => "255.255.255.255",
                Proto => 'udp',
Type => SOCK_DGRAM,
Timeout => 1);
my $localport = $sock->sockport();
    my $data = "irobotmcs";
syswrite($sock,$data);

my $datagram;
    while (1) {
        $sock->recv($datagram,100,0);
        print "Received datagram from ", $sock->peerhost, ": $datagram";
    }
Damit sehe ich in Wireshark den Broadcast rausgehen und ich sehe auch die Antwort des Staubsaugers. Allerdings funktioniert das Empfangen nicht mit dem Coding oben. Ich vermute mal, dass ich nach dem Öffnen des Client-Sockets einen Server-Socket aufmachen muss, der auf die Antwort lauscht. Dazu war jetzt aber noch keine Gelegenheit...
Gruß,
   Thorsten
RasPi
Heizkessel-Steuerung per Arduino und HTTPMOD
und einen Haufen Homematic (Wired)

Offline CoolTux

  • Developer
  • Hero Member
  • ****
  • Beiträge: 7812
  • fhem.cfg befreite Zone!
Antw:Broadcast und receive über UDP
« Antwort #4 am: 17 März 2017, 10:08:39 »
Ich denke mal eine zweite Socketverbindung brauchst Du nur wenn die Antwort über vom Roomba aufgemachte Verbindung kommt. Aber eigentlich sollte doch die bereits eröffnete Verbindung Verwendung finden. Um die Antwort zu erhalten fehlt Dir aber auf jeden Fall ein sysread.


Grüße
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.me/MOldenburg
Mein GitHub: https://github.com/LeonGaultier

Offline Thorsten Pferdekaemper

  • Tester
  • Hero Member
  • ****
  • Beiträge: 3353
  • Finger weg von der fhem.cfg
Antw:Broadcast und receive über UDP
« Antwort #5 am: 17 März 2017, 10:27:01 »
Ich denke mal eine zweite Socketverbindung brauchst Du nur wenn die Antwort über vom Roomba aufgemachte Verbindung kommt. Aber eigentlich sollte doch die bereits eröffnete Verbindung Verwendung finden. Um die Antwort zu erhalten fehlt Dir aber auf jeden Fall ein sysread.
Das kann ich auch mal ausprobieren, wobei mir nicht so ganz klar ist, wo jetzt der Unterschied zwischen sysread und recv ist.
Ich dachte inzwischen, dass man bei UDP sowieso keine "offene Verbindung" hat. ...aber ich werde mal verschiedene Varianten ausprobieren.
Danke&Gruß,
   Thorsten
RasPi
Heizkessel-Steuerung per Arduino und HTTPMOD
und einen Haufen Homematic (Wired)

Offline Thorsten Pferdekaemper

  • Tester
  • Hero Member
  • ****
  • Beiträge: 3353
  • Finger weg von der fhem.cfg
Antw:Broadcast und receive über UDP
« Antwort #6 am: 21 März 2017, 11:18:32 »
Hi,
nach viel rumprobieren:
    my $sock = new IO::Socket::INET(
                Proto => 'udp',
Type => SOCK_DGRAM,
Timeout => 60,
Broadcast => 1);   # TODO: error handling or die('Error opening socket.');
$sock->sockopt(SO_BROADCAST, 1);
    $sock->sockopt(SO_REUSEADDR, 1);
    my $data = "irobotmcs";
my $broadcastAddr = sockaddr_in( 5678, INADDR_BROADCAST );
    send( $sock, $data, 0,  $broadcastAddr );
my $datagram;
    while (1) {
sysread($sock,$datagram,200);
        print "Received datagram from ", $sock->peerhost, ": $datagram";
    }
    $sock->close();
Das funktioniert jetzt erwartungsgemäß, wobei ich auch nicht weiß, was man da noch weglassen kann. Anscheinend ist wichtig, dass man keine PeerAddr angibt.
Gruß,
   Thorsten
RasPi
Heizkessel-Steuerung per Arduino und HTTPMOD
und einen Haufen Homematic (Wired)