Probleme mit Socket-Programmierung



  • Hey,

    Ich steuere meinen Mikrocontroller über Ethernet an. Diesen kann ich unter der IP: 192.168.0.10 über den Port 4555 erreichen. Klappt auch alles wunderbar.
    Der Mikrocontroller empfängt über den Port 4555 und sendet die empfangen Dateien gleich wieder an dieselbe IP zurück auf dem Port 45175.

    Nun will ich eine 10mbyte große Datei über ethernet an meinen Mikrocontroller schicken mit pyhton:

    1.Versuch war:

    import socket,time
    
    datei=open("10_mb_file.txt","r")
    log=open("log.txt","w")
    
    #ip
    UPD_IP_Local="192.168.0.40"
    UPD_IP_Server="192.168.0.10"
    #ports
    UPD_Port_Listening= 45175
    UPD_Port_Send= 45555
    #create sockets
    sock_listening=socket.socket(socket.AF_INET,
    	        socket.SOCK_DGRAM)
    sock_listening.bind( (UPD_IP_Local,UPD_Port_Listening) )
    
    sock_writing=socket.socket(socket.AF_INET,
                             socket.SOCK_DGRAM)
    
    for line in datei:
     sock_writing.sendto(line, (UPD_IP_Server,UPD_Port_Send) )
     data, addr = sock_listening.recvfrom(128)
     log.write(data)
     print "recivbe: %s" %data
    

    Allerdings braucht der Mikrocontroller hier rund 33s, was ~317 kbyte/s entspräche. Das ist mir viel zu langsam, da der Mikrocontroller bis zu 100mbit/s unterstüzt.

    Kann es sein dass es an meinem Python Programm liegt? Weil ich immer zeilenweise einlese? Vielleicht muss ich ja größere Strings auf einmal schicken?

    Versuch 2:
    Ein 50000 großen String 10000mal senden

    import socket,time
    
    #ip
    UPD_IP_Server="192.168.0.10"
    #ports
    
    UPD_Port_Send= 45555
    
    sock_writing=socket.socket(socket.AF_INET,
                             socket.SOCK_DGRAM)
    sock_writing.sendto("ahllo", (UPD_IP_Server,UPD_Port_Send) )
    
    i=0
    a=0
    while a<=10000:
       s='hallo'
       a=a+1
       i=0
       while i<=50000:
         i=i+1
         s=s+"i"
       #print s
       sock_writing.sendto(s, (UPD_IP_Server,UPD_Port_Send) )
    

    Zeit: 83s 👍

    und nebenei auf den Port lauschen:

    import socket
    
    def receive_test() :
        #Local ip
        UDP_IP="192.168.0.40"
        #Local port listening for packets
        UDP_PORT= 45175
        #Create a socket
        sock = socket.socket( socket.AF_INET, # Internet
                          socket.SOCK_DGRAM ) # UDP
        sock.bind( (UDP_IP,UDP_PORT) )
        #Wait for data and print it.
        while True:
            data, addr = sock.recvfrom( 128 ) # buffer size is 1024 bytes
            print "received message: %s" % data
    
    receive_test()
    

    Problem ist hier, dass er mir nichts anzeigt, da anscheinend der gesendete String zu groß ist?

    Gibt es eine Möglichkeit, dass ich die ein-kommenden Dateien vom Socket, egal wie groß einfach in einen String oder ähnliches schreibe ?

    Vielen Dank



  • Bei deiner ersten Variante sendest du eine Zeile zum µC, der schickt sie zurück und du empfängst sie. Erst danach wird die nächste Zeile gesendet.
    Hier spielt auch die Zeilenlänge bzw die Anzahl Zeilen eine Rolle.

    Bei der zweiten Variante sendest du alles nacheinander und empfängst parallel dazu. Daher wird das Netz besser ausgelastet. (Hier kann man auch schön mit select beides in einem Programm machen.)

    So große UDP-Pakete sind nicht unbedingt sinnvoll. Was die Implementierung des µC kann, solltest du am ehesten wissen. ("mein Mikrocontroller" => von dir programmiert?)

    Versuch es halt mal mit kleineren Paketen. 512-Nutzbytes sollten eigentlich von jeder Implementierung verarbeitbar sein.

    Edit:
    Folgendes Program sendet immer Daten wenn es kann und empfängt immer Daten wenn sie anliegen. Sind alle Daten gesendet, beendet es sich. (Die letzten Pakete werden nicht wieder empfangen.)

    import socket,time,select
    
    serverIp = "192.168.0.10"
    answerUdpPort = 45175
    serverUdpPort = 45555
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind(("", answerUdpPort))
    
    data = 'x' * 512
    sent = 0
    countSent = 0
    countRecv = 0
    
    while sent < 10 * 1024 * 1024:
    
    	readable, writable, exceptional = select.select([sock], [sock], [sock])
    
    	for s in readable:
    		answer, addr = s.recvfrom(10240)
    		countRecv = countRecv + 1
    
    	for s in writable:
    		s.sendto(data, (serverIp, serverUdpPort))
    		sent = sent + len(data)
    		countSent = countSent + 1
    
    print "Sent: %i ;Recv: %i" %(countSent, countRecv)
    

    Je nach Paketgröße ergeben sich unterschiedliche Übertragungsgeschwindigkeiten der Nutzdaten (in einem GBit LAN zwischen zwei PCs):
    1 57 KiB/s
    10 552 KiB/s
    100 5.6 MiB/s
    1000 54 MiB/s
    10000 100 MiB/s
    50000 100 MiB/s



  • Hey,

    vielen lieben Dank für die Antwort.

    Es ist nicht so ganz mein Mikrocontroller. Ich versuche nur ein Beispiel, diesen UPD-Ping Server welcher schon drauf ist zu verifizieren.
    Nur Blick ich da allgemein noch nicht ganz durch 😃

    Zu deinem Beispiel:

    ....
    data='x'*512
    ....
    

    Ich habe noch ein print eingefügt, damit ich die Ausgabe sehe:
    Ausgabe:

    x.....x
    Sent: 20480 ;Recv: 6
    

    dann habe ich folgendes geändert:

    ...
    data='x'*1024
    ...
    

    Ausgabe:

    x....x
    Sent: 10486 ;Recv: 6
    

    wenn ich

    ...
    date='x'*2000
    ...
    

    kommt folgendes:

    Sent: 5243 ;Recv: 0
    

    Kannst du mir kurz die letzte Ausgabe erklären? Schafft er es nicht 2000 Zeichen wieder zurück zu schicken oder wie?



  • Ich habe das Programm jetzt auf meine Bedürfnisse abgestimmt.
    Ich möchte ja eine 10mbyte große Datei an den Mikrokontroller schicken und wieder empfangen.

    import socket,time,select,sys
    
    BUFFSIZE=1024
    
    datei=open("10_mb_file.txt","r")
    log=open("log.txt","w")
    
    serverIp = "192.168.0.10"
    answerUdpPort = 45175
    serverUdpPort = 45555
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind(("", answerUdpPort))
    
    dateistring=''
    for line in datei:
      dateistring=dateistring+line
    
    data = 'x' * 512
    sent = 0
    countSent = 0
    countRecv = 0
    i=0
    while i*BUFFSIZE<len(dateistring):
      tausend=0
      sentstring=''
      while tausend<BUFFSIZE:
        sentstring=sentstring+dateistring[tausend+i*BUFFSIZE]
        tausend=tausend+1
    
      readable, writable, exceptional = select.select([sock], [sock], [sock])
    
      for s in readable:
          answer, addr = s.recvfrom(10240)
          log.write(answer)
          countRecv = countRecv + 1
    
      for s in writable:
          s.sendto(sentstring, (serverIp, serverUdpPort))
          countSent = countSent + 1
      i=i+1
    print "Sent: %i ;Recv: %i" %(countSent, countRecv)
    

    Ausgabe nach Test:

    Sent: 10240 ;Recv: 10237
    

    Wieso fehlen mir empfangene Packete? Wie bekomme ich die her?

    Das ganze läuft aber rasend schnell in ~4,2s 👍 👍



  • Wikipedia: User Datagram Protocol:
    UDP ist ein verbindungsloses, nicht-zuverlässiges und ungesichertes wie auch ungeschütztes Übertragungsprotokoll.

    Du kannst dich also nicht darauf verlassen, dass alles, was du sendest auch ankommt. Und auch nicht, dass es in der selben Reihenfolge ankommt! (In einem kleinen LAN ist das zwar meistens der Fall, garantiert ist es aber nicht.)

    Außerdem wird das Program beendet, sobald alles gesendet wurde. Das heißt am Ende sind noch Pakete "unterwegs", aber das Programm schon beendet.

    select bietet einen Timeout. Du kannst also auch einfach noch warten und erst wenn 1s oder so nichtsmehr ankommt, das Programm beenden.

    Das bei dem Test mit x…x kaum etwas ankam, kann einfach daran liegen, dass der µC nicht hinterher kam. In Kombination mit UDP kann es halt einen riesen Verlust darstellen.

    Um die maximale Größe herauszufinden, kannst du immer ein Paket senden und mit der binären Suche je nachdem ob du eine Antwort bekommst oder nicht den Suchraum der Paketgrößen halbieren. (Bei ~2^16 Möglichkeiten kannst du aber auch einfach linear suchen.)


Anmelden zum Antworten