Problem mit UDP Paketen

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

Hallo,

ich bin noch neu in der Socketprogrammierung daher hoffe ich dass die Frage nicht zu banal ist, ich habe folgende Gegebenheiten:

Im Netzwerk befinden sich Teilnehmer die per UDP Boradcast an den Port 6811 getriggert werden ihre ID zu senden.
dies habe ich mit Python schon realisiert, jetzt möchte ich eine Liste der IPs aller antwortenden Teilnehmern speichern. Leider kommen nicht alle Antworten bei Python an.
Wenn ich Wireshark mitlaufen lasse sehe ich dass die Antworten von allen Teilnehmern auf jeden Fall gesendet werden, ich würde jetzt mal vermuten dass wenn Wireshark die Pakete sieht sie auch 100% an meinem Netzwerkinterface ankommen und somit auch an Python geschickt werden.

hier ist mein Code, ich habe den Broadcast zum testen in eine Schleife gepackt, die ich 4 mal durchlaufen lasse, ich dachte damit könnte ich alle Teilnehmer erwischen, funktioniert aber nicht:

Code: Alles auswählen

def sendBroadcast(self):
        
        ipList=[]
        b=0               
        
        while 1:
         b=b+1
         s = socket(AF_INET, SOCK_DGRAM)
         s.setsockopt(SOL_SOCKET,SO_BROADCAST,SOL_UDP)
         s.sendto('\x00\x00\x00\x00\x00\x11',('255.255.255.255',6811))
         usedPort=s.getsockname()[1] #get Port number the Broadcast has been sent from
         s.close()   
        
         host = ""        
         buf = 2048
         addr = (host,usedPort)
        
         UDPSock = socket(AF_INET,SOCK_DGRAM, SOL_UDP)
         UDPSock.bind(addr)
         print "check for received data"  
         data,addr = UDPSock.recvfrom(buf)
         print "received data"
         if data:
             double=0
             if ipList:
                 for a in range(len(ipList)):
                     if addr[0]==ipList[a]:
                         double=1
                         print "ip is already in list"
                         break
                     else:
                         double=0
                    
             if double==0:
               print "appending"            
               ipList.append(addr[0])
               print "\
        Received message '", data,"'"
         print "closing receive socket"
         UDPSock.close()
         if b==4:
              break       
das ganze läuft auf einer Win XP Maschine.
Was mich interessieren würde, wenn ich recvfrom() ausführe

Code: Alles auswählen

data,addr = UDPSock.recvfrom(buf)
was passiert dann wenn mehrere Pakete von verschiedenen Teilnehmern unmittelbar hintereinander eintreffen, verwirft Python einfach alle bis auf das erste oder werden diese zwischengespeichert und können mit einer anderen Anfrage abgerufen werden??

Vielen Dank für eure Hilfe
Zuletzt geändert von Anonymous am Montag 9. Juli 2012, 13:04, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@red_dust: Mal davon abgesehen das UDP nicht garantiert das Pakete ankommen ist der Code von Dir auch ein wenig abenteuerlich. Es wird viermal ein Broadcast gesendet und genau vier mal ein Socket zum Empfangen aufgemacht und ausgelesen. Ich sehe jetzt nicht wie man damit mehr als vier Antworten lesen kann, wobei die auch jedes mal vom gleichen Sender kommen könnten. Du solltest vielleicht lieber *einen* Broadcast senden und dann eine Weile *alle* Pakete lesen die darauf als Antwort kommen. Was bei Dir ausserdem passieren kann, ist das Pakete ankommen während Du mit Deinem Empfangs-Socket nicht verbunden bist, zum Beispiel wenn eine Antwort nach dem Broadcast aber vor dem ``UDPSock.bind(addr)`` kommt. Die verlierst Du.

Zur `recfrom()`-Frage: Du kannst mehrere Pakete lesen, aber nur wenn Du das Socket nicht zwischendurch schliesst.

Wenn man Wahheitswerte meint, sollte man `True` und `False` statt 1 und 0 verwenden.

Statt einer Liste mit den IPs könntest Du ein `set()` verwenden und Dir damit den gruseligen Code in ``if data:`` sparen. Das sieht sehr krude und wenig „pythonisch” aus. ``for i in range(len(obj)):`` um dann mit `i` auf Elemente von `obj` zuzugreifen ist in Python ein Anti-Pattern. Man kann *direkt* über die Elemente von Sequenzen iterieren, ohne einen Umweg über Indizes gehen zu müssen. In diesem Fall hätte man das aber auch ganz simpel mit dem ``in``-Operator prüfen können, statt selbst so eine umständliche Schleife zu schreiben. Der ganze Code in ``if data:`` könnte so aussehen:

Code: Alles auswählen

            ip = addr[0]
            if ip in ip_list:
                print 'ip is already in list'
            else
                print 'appending'
                ip_list.append(ip)
                print 'Received message %r' % ip
Du bist total inkonsistent mit der Einrückung. Vier Leerzeichen pro Ebene sind Konvention — aber mindestens *einheitlich* sollte es sein.

Immer wenn man vor Eintritt in eine Schleife weiss wie oft sie durchlaufen werden soll, zieht man normalerweise die ``for``-Schleife der ``while``-Schleife vor.
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

Hi BlackJack,

vielen Dank für deine Antwort, trotz meines stümperhaften Programmierstils :-)

Dass UDP verwendet wird kann ich leider nicht Ändern, da ich keinen Einfluss auf die Programmierung der Teilnehmer habe. Dass Pakete verloren gehen können ist mir bewusst, deshalb wollte ich den Broadcast 4 mal hintereinander ausführen und die Antwortenden IPs der 4 Broadcasts in die Liste speichern.
Das habe ich aber falsch programmiert wie ich jetzt sehe.

Ich habe den Code mal angepasst und auch die Python Konventionen berücksichtigt

Code: Alles auswählen

    def sendBroadcast(self):
        
        ipList = []
        addr1=("", 0)
        buf = 2048               
     
        s = socket(AF_INET, SOCK_DGRAM)
        s.setsockopt(SOL_SOCKET, SO_BROADCAST, SOL_UDP)
        s.bind(addr1)
        print "Portnumber:", s.getsockname()[1] #get Port number the Broadcast has been sent from 
        s.sendto('\x00\x00\x00\x00\x00\x11', ('255.255.255.255', 6811))
          
        print "check for received data"  
        data, addr = s.recvfrom(buf)
        print "received data"
        ip = addr[0]
        if data:
            if ip in ipList:                
                print "ip is already in list"               
            else:
                print "appending ip"
                ipList.append(ip)
                print "\
        Received message '", data, "'"
        print "closing receive socket"
        s.close()
es würde im Prinzip reichen wenn ich den Broadcast abschicke und dann 2 Sekunden lang auf Antworten horche und diese abspeichere, evtl. 1 mal wiederhole um sicher zu gehen dass kein Paket verloren gegangen ist (wie gesagt in Wireshark sehe ich immer alle Teilnehmer antworten, die wahrscheinlichkeit das wirklich ein Paket verloren geht halte ich daher für sehr gering).
Wie ich das realisieren soll ist mir aber noch etwas schleierhaft, hast du noch einen Wink für mich?

Danke
Zuletzt geändert von Anonymous am Montag 9. Juli 2012, 15:36, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@red_dust: Man kann für Sockets einen „timeout” setzen, oder mit dem `select`-Modul arbeiten.

Ich sehe übrigens keinen Grund im Quelltext warum das eine Methode sein sollte‽

Es gibt noch ein paar mehr Konventionen neben der Einrückung: PEP 8 -- Style Guide for Python Code.
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

So ich hoffe das ist eine halbwegs saubere Lösung:

Code: Alles auswählen

    def sendBroadcast(self):
        
        ipList = []
        addr1=("", 0)
        buf = 2048
                
        s = socket(AF_INET, SOCK_DGRAM)
        s.setsockopt(SOL_SOCKET, SO_BROADCAST, SOL_UDP)
       
        s.bind(addr1)
        print "Portnumber:", s.getsockname()[1] #get Port number the Broadcast has been sent from 
        s.sendto('\x00\x00\x00\x00\x00\x11', ('255.255.255.255', 6811))
        s.settimeout(2)
               
        while True:   
            print "check for received data"  
            try:
                data, addr = s.recvfrom(buf)
            except timeout:
                break
            print "received data"
            ip = addr[0]
            if data:
                if ip in ipList:                
                    print "ip is already in list"               
                else:
                    print "appending ip"
                    ipList.append(ip)
                    print "\
            Received message '", data, "'"
            
        print "closing receive socket"
        s.close()
        return ipList
Antworten