UDP Server

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Muck22
User
Beiträge: 32
Registriert: Donnerstag 25. September 2014, 05:58

Hallo zusammen,

ich hab ein Problem mit diesem Code!

Code: Alles auswählen

import socket

# A UDP server

# Set up a UDP server
UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

# Listen on port 21567
# (to all IP addresses on this system)
listen_addr = ("",5000)
UDPSock.bind(listen_addr)

# Report on all data packets received and
# where they came from in each case (as this is
# UDP, each may be from a different source and it's
# up to the server to sort this out!)
while True:
        data,addr = UDPSock.recvfrom(1024)
        print data.strip(),addr
und zwar, wenn ich eine Programm mit einer GUI schreiben will in dem ich beim starten ein wxframe öffne, und gleichzeitig der oben stehende Code ausgeführt werden soll weiss ich nie wo ich den hinpacken soll.
Ich möchte das der Server empfangsbereit ist sobald das wxframe komplett aufgebaut ist.
Wenn ich das wxframe aufbaue und den Code einfach darunter setze kann das ja auch nicht richtig sein.
Aber ich will den Code ja auch nicht in eine Funktion setzten z.B. wenn ich einen Button betätige.
Wie macht man denn sowas am besten?
(Sorry, aber ich weiss nicht wie ich es anders erklären soll)! :(
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Du musst deinen Netzwerkcode in einem anderen Thread laufen lassen als die GUI. Zu dem Thema gibt es hier im Forum schon einige Beiträge, da könntest du ein wenig stöbern. Das ganze ist allerdings ein wenig Trickreich, da du aus deinem Netzwerk-Thread nicht einfach so auf die GUI zugreifen kannst. Du darfst zum Beispiel nicht einfach den empfangen Text in einem Feld setzen. Aber wie gesagt, suche zu dem Thema einfach ein wenig.

Ich würde dir empfehlen ein kleines lauffähiges Beispielprogramm zu basteln und das dann hier zu zeigen. So lassen sich die schlimmsten Fehler gleich ausbessern und du musst dich nicht damit rumschlagen, dass dir deine GUI spontan um die Ohren fliegt.
Das Leben ist wie ein Tennisball.
Muck22
User
Beiträge: 32
Registriert: Donnerstag 25. September 2014, 05:58

Ok!
Alle Hoffnungen dahin!
Ich dachte wirklich das ich die empfangenen Daten einfach so in die Gui packen könnte.
Hatte mir das ehrlich gesagt einfacher vorgestellt! :wink:

Aber ich bleibe dran!!!!! :P
Muck22
User
Beiträge: 32
Registriert: Donnerstag 25. September 2014, 05:58

Ich würde dir empfehlen ein kleines lauffähiges Beispielprogramm zu basteln und das dann hier zu zeigen. So lassen sich die schlimmsten Fehler gleich ausbessern und du musst dich nicht damit rumschlagen, dass dir deine GUI spontan um die Ohren fliegt.[/quote]

@EyDu, so jetzt versuche ich die empfangene Nachricht in ein TextCtrl zu bekommen weiss aber nicht wie! :(

Code: Alles auswählen

import wx
import os
import time
import threading
import socket

class Reader(threading.Thread):

    def __init__(this, udp_sock):
        threading.Thread.__init__(this)
        this.udp_sock = udp_sock
        this.running = 0
        
    def run(this):
        this.running = 1
        while this.running:
            try:
                text, addr = this.udp_sock.recvfrom(8000)
                print text
            except socket.error, e:
                print e
           
   
udp_sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_sock.bind(("", 5555))

reader = Reader(udp_sock)
reader.start()

while not reader.running:
    time.sleep(1.0)

class Size(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(450, 300))
        self.SetIcon(wx.Icon('icons/wwww.ico', wx.BITMAP_TYPE_ICO))
        self.SetBackgroundColour((238,207,161))
        self.Centre()
        self.Show(True)
        
        #Erstellen eines weiteren TextCtrl
        self.tc = wx.TextCtrl(self, -1, size=(150, 22), pos=(180, 100))
        
        #Erstellen eines weiteren TextCtrl
        self.tc1 = wx.TextCtrl(self, -1, size=(150, 22), pos=(180, 150))
        
        #Butten erstellen
        but1 = wx.Button(self, -1, 'senden', pos=(15, 90))
        
        #Button binden
        self.Bind(wx.EVT_BUTTON, self.send, id=but1.GetId())
        
        #Label erzeugen
        wx. StaticText (self , label ="Nachricht eingeben!", pos=(180, 83))
        
        #icon erzeugen
        icon = wx.StaticBitmap(self, -1, wx.Bitmap('icons/send.png'), pos=(110, 83))
        
        icon = wx.StaticBitmap(self, -1, wx.Bitmap('icons/yyyy_logo.png'), pos=(15, 120))
        
        #Funktionen erstellen
    def send(self, event):
        #print 'tu etwas'
        #while 1:
            #time.sleep(1.0)
            #print "!!"
            msg = self.tc.GetValue()
            #udp_sock.sendto("hi, das ist meine Nachricht!", ('192.168.2.105', 5555))
            udp_sock.sendto(str(msg), ('localhost', 5555))
            #print "   done."

        
app = wx.App()
Size(None, -1, 'xxxx Infotool')
app.MainLoop()
BlackJack

@Muck22: Ist gibt ja eine Menge Konventionen gegen die man verstossen kann, aber `self` nicht `self` zu nennen ist etwas was man überhaupt nicht tun sollte. Da kommt auch echt kaum jemand drauf.

Das `os`-Modul wird importiert aber nicht verwendet.

Code auf Modulebene solle nur Konstanten, Funktionen, und Klassen definieren. Das Hauptprogramm gehört in eine Funktion. Und das vermischen von Hauptprogramm-Fragmenten mit Funktions- oder Klassendefinitionen macht das ganze noch mal ein Stück unübersichtlicher.

Wenn man einen Wahrheitswert meint sollte man keine Zahlen verwenden. Bei einer Zuweisung von 1 oder 0 fragt sich der Leser ob auch 2 oder 3 eine Bedeutung haben könnte, wogegen bei `True` oder `False` klar ist, dass 2 oder 3 eher nicht in Frage kommen.

Namen sollte nicht aus kryptischen Abkürzungen bestehen oder gar nur aus einzelnen Buchstaben, solange der Gültigkeitsbereich nicht sehr beschränkt ist oder die Abkürzung allgemein gebräuchlich ist. Es ist ja nicht so als wenn der Speicher knapp wäre oder man pro Buchstabe Geld bezahlen müsste das man gezwungen ist `e` statt `error` zu schreiben. ;-) Das gleiche gilt auch für `message` statt `msg` oder `button` statt `but1`. Beim letzteren ist die Nummer ein Warnzeichen. Wenn man anfängt Namen zu nummerieren macht man in der Regel etwas falsch.

Die GUI-Elemente haben insgesamt schlechte Namen, insbesondere die, die als Attribute auf dem Objekt gesetzt werden, sollten dem Leser vermitteln was sie *bedeuten*. `tc` und `tc1` tun das nicht. Selbst `text_control` wäre ein schlechter Name weil man dann immer noch nicht weiss wozu dieses Texteingabefeld da ist. Zur Eingabe von Nachrichten? Zur Anzeige von Nachrichten? Anzeige von Fehlern? Irgend etwas anderes?

Warum die GUI-Klasse `Size` heisst, ist mir ein absolutes Rätsel.

Bei IDs würde ich statt -1 die Konstante `wx.ID_ANY` übergeben, dann weiss der Leser sofort was das Argument für eine Bedeutung hat.

Kommentare sollten einen Mehrwert zum Quelltext darstellen und dem Leser nicht erzählen *was* gemacht wird, das sieht er ja schon am Code, sondern *warum* der Code das macht was er macht, sofern das nicht ebenfalls einfach am Code ablesbar ist.

Das Layout regelt man in `wx` am besten über Sizer, *nicht* über absolute Grössen und Positionsangaben. Das kann man heutzutage nicht mehr machen ohne Gefahr zu laufen dass die GUI auf anderen Rechner als dem auf dem sie erstellt wurde, besch… aussieht oder gar unbenutzbar wird wenn Texte nicht mehr in den Platz passen oder so klein sind dass man sie nicht mehr vernünftig lesen kann. Die Zeiten das jedes Gerät einen Monitor hat dessen Auflösung und Masse ungefähr 96 DPI ergeben sind lange vorbei.

Beim `Bind()` ist es eher ungewöhnlich eine `id` für eine Schaltfläche zu übergeben. Man kann als drittes Argument einfach die Schaltfläche selbst als Argument angeben.

In Funktionen und Methoden sollte man ausser auf Konstanten auf nichts zugreifen was nicht als Argument übergeben wird. In `send()` darf man also nicht einfach auf `udp_sock` zugreifen was auf magische Weise irgendwo in der Umgebung zu existieren scheint.

Den Inhalt vom Texteingabefeld einfach mit `str()` in einen Bytestring zu wandeln wird dir um die Ohren fliegen sobald dort Zeichen ausserhalb des ASCII-Wertebereichs eingegeben werden. An der Stelle sollte explizit von Unicode nach Bytes kodieren. UTF-8 bietet sich als Kodierung an.

Bei dem Thread sollte man das `daemon`-Attribut auf `True` setzen, damit das Programm auch tatsächlich endet wenn das Fenster geschlossen wird, und nicht auf das Ende des Threads gewartet wird.

Die ``while not reader.running:``-Schleife verstehe ich nicht‽ Wozu soll die gut sein?

Über Threads hinweg sichere Aufrufe in `wx` bekommt man mit `wx.CallAfter()` hin. Ich habe mal die minimal nötigsten Änderungen in der Richtung am Programm vorgenommen, wobei die Aufteilung des Codes so nicht wirklich schön ist. Man sollte die Kommunikation zum Beispiel komplett in einer Klasse kapseln die weder Thread noch GUI ist und die Low-Level-Einzelteile wie Sockets nicht in der Gegend herum reichen.

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
import socket
import threading

import wx

 
class Reader(threading.Thread):
 
    def __init__(self, udp_sock, callback=lambda message: None):
        threading.Thread.__init__(self)
        self.udp_sock = udp_sock
        self.callback = callback
        self.running = False
       
    def run(self):
        self.running = True
        while self.running:
            try:
                message, _ = self.udp_sock.recvfrom(8000)
                self.callback(message)
            except socket.error, error:
                print(error)


class MainWindow(wx.Frame):
    def __init__(self, parent, title, reader, udp_sock):
        wx.Frame.__init__(self, parent, wx.ID_ANY, title, size=(450, 300))
        self.reader = reader
        self.udp_sock = udp_sock

        self.SetIcon(wx.Icon('icons/wwww.ico', wx.BITMAP_TYPE_ICO))
        self.SetBackgroundColour((238, 207, 161))
        self.Centre()
        self.Show(True)
       
        send_button = wx.Button(self, wx.ID_ANY, 'senden', pos=(15, 90))
        wx.StaticText(self, label="Nachricht eingeben!", pos=(180, 83))
        self.text_input = wx.TextCtrl(
            self, wx.ID_ANY, size=(150, 22), pos=(180, 100)
        )
        self.text_output = wx.TextCtrl(
            self, wx.ID_ANY, size=(150, 22), pos=(180, 150)
        )
        wx.StaticBitmap(
            self, wx.ID_ANY, wx.Bitmap('icons/send.png'), pos=(110, 83)
        )
        wx.StaticBitmap(
            self, wx.ID_ANY, wx.Bitmap('icons/yyyy_logo.png'), pos=(15, 120)
        )
       
        self.Bind(wx.EVT_BUTTON, self.send, send_button)
        self.reader.callback = self.receive
       
    def send(self, _event):
        message = self.text_input.GetValue()
        self.udp_sock.sendto(message.encode('utf8'), ('localhost', 5555))

    def receive(self, message):
        wx.CallAfter(self.text_output.SetValue, message.decode('utf8'))
 

def main():
    udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_sock.bind(("", 5555))
     
    reader = Reader(udp_sock)
    reader.daemon = True
    reader.start()

    app = wx.App()
    MainWindow(None, 'xxxx Infotool', reader, udp_sock)
    app.MainLoop()


if __name__ == '__main__':
    main()
Muck22
User
Beiträge: 32
Registriert: Donnerstag 25. September 2014, 05:58

:D
Also erst mal danke für die Antwort.

Der Code ist eigentlich ein Gemisch aus einem Programm das ich zuvor gemacht hatte und den Versuch eine UDP Verbindung mit einer GUI zu verbinden!
Deswegen auch importe die gar nicht dahin gehören und all der andere Kram.

Ganz ehrlich das ist so meine Methode Sachen zu kopieren und zu probieren ob was funktioniert oder nicht (klappt nicht immer).
Wenn aber doch, schau ich mir die Sache an und versuche dahinter zu kommen wie es funktioniert und nehme das mehr oder weniger als Vorlage für das eigentliche (wenn man so will ins reine geschriebene).

Ich werde versuchen es besser zu machen wenn ich verstehe was ich da mache! :)

Danke für die Überarbeitung, Dein Code macht genau was er soll.
Jetzt muss ich nur noch verstehen was da im einzelnen so abläuft (Versuche micht mit Python noch nicht so lange wie man merkt)! :D
Antworten