UDP Socket Projekt - Sinusfunktion

Du hast eine Idee für ein Projekt?
Antworten
ajs5499
User
Beiträge: 4
Registriert: Montag 9. Mai 2022, 14:15

Hallo Leute,

ich sitze derzeit an einem kleinen Projekt und zwar möchte ich mithilfe des UDP Sockets in Python zwei Teilprozesse, die natürlich miteinander kommunizieren können. Und zwar möchte ich am Ende mithilfe dessen einen Sinusverlauf erstellen. Ich möchte, dass der erste Prozess Zeit und Amplitude an den zweiten Prozess weitergibt und das schrittweise. Und der zweite Prozess soll die Daten aufnehmen und dann die Punkte plotten. Leider habe ich da ein paar Schwierigkeiten und würde mich über Tipps von euch freuen :)

Was ich bisher habe:
- Erster Prozess:

import socket
import numpy as np
import matplotlib.pyplot as plot
import pickle

if __name__ == "__main__":
host = "127.0.0.1"
port = 4455
addr = (host, port)
HEADERSIZE = 10

# Create a UDP socket at client side

UDPClientSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)

# Send to server using created UDP socket
time = 0
data = dict()
data['time'] = []
data['amplitude'] = []

while True:

amplitude = np.sin(time)

data['time'].append(time)
data['amplitude'].append(amplitude)

msg = pickle.dumps(data)
print(f'{len(msg)}')
print(f'{len(msg):<{HEADERSIZE}}')
msg = bytes(f'{len(msg):<{HEADERSIZE}}', 'utf-8') + msg

time = time + 0.1

UDPClientSocket.sendto(msg, addr)

msg, addr = UDPClientSocket.recvfrom(2048)

print(f"Server: {msg}")

- Zweiter Prozess:

import socket
import numpy as np
import matplotlib.pyplot as plot
import pickle

if __name__ == "__main__":
host = "127.0.0.1"
port = 4455
HEADERSIZE = 10

# Create a datagram socket

UDPServerSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)

# Bind to address and ip

UDPServerSocket.bind((host, port))

print("UDP server up and listening")

# Listen for incoming datagrams

while True:
msg, addr = UDPServerSocket.recvfrom(2048)

print(f"Client: {msg}")

# msg entpacken

msg = pickle.dumps(msg).decode()

plot.plot(msg)

# Give a title for the sine wave plot

plot.title('Sine wave')

# Give x axis label for the sine wave plot

plot.xlabel('Time')

# Give y axis label for the sine wave plot

plot.ylabel('Amplitude = sin(time)')

plot.grid(True, which='both')

plot.axhline(y=0, color='k')

# Display the sine wave

plot.show()

# Sending a reply to client

UDPServerSocket.sendto(msg, addr)

Wenn ich das so ausführe, wird mir folgende Fehlermeldung angezeigt: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte

Ich würde mich mega freuen, wenn mir da jemand helfen könnte.
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist alles Gurke. Warum baust du ein Protokoll mit Länge und dann pickle, obwohl UDP message basiert immer eine vollständige Nachricht liefert? Die dekodierung deines Protokolls im Server fehlt dann aber. Und zweimal dumps ist auch falsch. Du musst einmal loads nutzen. Ein decode danach ist auch Quatsch, die Nachricht ist ja typisiert. Zu guter letzt: warum der Rückkanal, und wenn der nötig ist, warum dann kein TCP?
ajs5499
User
Beiträge: 4
Registriert: Montag 9. Mai 2022, 14:15

__deets__ hat geschrieben: Montag 9. Mai 2022, 14:58 Das ist alles Gurke. Warum baust du ein Protokoll mit Länge und dann pickle, obwohl UDP message basiert immer eine vollständige Nachricht liefert? Die dekodierung deines Protokolls im Server fehlt dann aber. Und zweimal dumps ist auch falsch. Du musst einmal loads nutzen. Ein decode danach ist auch Quatsch, die Nachricht ist ja typisiert. Zu guter letzt: warum der Rückkanal, und wenn der nötig ist, warum dann kein TCP?
Habe meinen Code noch einmal überarbeitet, weil wie du schon sagst das alles irgendwie keinen Sinn ergeben (bin seit einem Monat mit Python am arbeiten, deswegen nicht zu streng sein :D)
Nur habe ich jetzt eine neue Fehlermeldung, vielleicht kannst du mir da auch weiterhelfen: _pickle.UnpicklingError: could not find MARK
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Bitte Code zeigen, aus es gibt zu jeder Fehlermeldung unendlich viele Moeglichkeiten, wie die erzeugt worden sein kann.
ajs5499
User
Beiträge: 4
Registriert: Montag 9. Mai 2022, 14:15

__deets__ hat geschrieben: Dienstag 10. Mai 2022, 10:11 Bitte Code zeigen, aus es gibt zu jeder Fehlermeldung unendlich viele Moeglichkeiten, wie die erzeugt worden sein kann.
Ich habe den Code jetzt noch einmal komplett umgeändert. Der sieht jetzt folgendermaßen aus:
-Server:
import socket
import numpy as np
import matplotlib.pyplot as plot
import pickle

if __name__ == "__main__":
host = "127.0.0.1"
port = 4455
HEADERSIZE = 10

# Create a socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

# Bind to address and ip

s.bind((host, port))
s.listen()

print("UDP server up and listening")

conn, addr = s.accept()

# Listen for incoming datagrams
with conn:
print(f'Connected by {addr}')
while True:

received = conn.recv(4096)

print(f"Client: {received}")

# msg entpacken

data_variable = pickle.loads(received)

print(data_variable)

plot.plot(data_variable)

# Give a title for the sine wave plot

plot.title('Sine wave')

# Give x axis label for the sine wave plot

plot.xlabel('Time')

# Give y axis label for the sine wave plot

plot.ylabel('Amplitude = sin(time)')

plot.grid(True, which='both')

plot.axhline(y=0, color='k')

# Display the sine wave

plot.show()

- Client:
import socket
import numpy as np
import matplotlib.pyplot as plot
import pickle
import time

if __name__ == "__main__":
host = "127.0.0.1"
port = 4455
addr = (host, port)
HEADERSIZE = 10

# Create a UDP socket at client side

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

s.connect((host, port))

# Send to server using created UDP socket
time = 1
data = dict()
data['time'] = []
data['amplitude'] = []

while True:

amplitude = np.sin(time)

data['time'].append(time)
data['amplitude'].append(amplitude)

msg = pickle.dumps(data)
print(f'{len(msg)}')
print(f'{len(msg):<{HEADERSIZE}}')
msg = bytes(f'{len(msg):<{HEADERSIZE}}', 'utf-8') + msg

time = time + 0.1

s.send(msg)
print(f"Server: {msg}")

Dennoch wird mir weiterhin auf Serverseite der oben genannte Fehler angezeigt (data_variable = pickle.loads(data)
_pickle.UnpicklingError: could not find MARK)
Benutzeravatar
sparrow
User
Beiträge: 4144
Registriert: Freitag 17. April 2009, 10:28

Bitte setz den Code doch in Code-Tags. Die erscheinen automatisch, wenn du im vollständigen Editor den </>-Button drückst. Zwichen die Tags gehört der Code.
So geht die Einrückung verloren - und die ist bei Python nun einmal wichtig.
ajs5499
User
Beiträge: 4
Registriert: Montag 9. Mai 2022, 14:15

Code: Alles auswählen

[code]
[/code]
sparrow hat geschrieben: Mittwoch 11. Mai 2022, 09:18 Bitte setz den Code doch in Code-Tags. Die erscheinen automatisch, wenn du im vollständigen Editor den </>-Button drückst. Zwichen die Tags gehört der Code.
So geht die Einrückung verloren - und die ist bei Python nun einmal wichtig.
Danke für den Tipp :)

Server:

Code: Alles auswählen

import socket
import numpy as np
import matplotlib.pyplot as plot
import pickle

if __name__ == "__main__":
    host = "127.0.0.1"
    port = 4455
    HEADERSIZE = 10

    # Create a socket

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

        # Bind to address and ip

        s.bind((host, port))
        s.listen()

        print("UDP server up and listening")

        conn, addr = s.accept()

        # Listen for incoming datagrams
        with conn:
            print(f'Connected by {addr}')
            while True:

                received = conn.recv(4096)

                print(f"Client: {received}")

                # msg entpacken

                data_variable = pickle.loads(received)

                print(data_variable)

                plot.plot(data_variable)

                # Give a title for the sine wave plot

                plot.title('Sine wave')

                # Give x axis label for the sine wave plot

                plot.xlabel('Time')

                # Give y axis label for the sine wave plot

                plot.ylabel('Amplitude = sin(time)')

                plot.grid(True, which='both')

                plot.axhline(y=0, color='k')

                # Display the sine wave

                plot.show()
                
Client:

Code: Alles auswählen

import socket
import numpy as np
import matplotlib.pyplot as plot
import pickle
import time

if __name__ == "__main__":
    host = "127.0.0.1"
    port = 4455
    addr = (host, port)
    HEADERSIZE = 10

    # Create a UDP socket at client side

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

        s.connect((host, port))

        # Send to server using created UDP socket
        time = 1
        data = dict()
        data['time'] = []
        data['amplitude'] = []

        while True:

            amplitude = np.sin(time)

            data['time'].append(time)
            data['amplitude'].append(amplitude)

            msg = pickle.dumps(data)
            print(f'{len(msg)}')
            print(f'{len(msg):<{HEADERSIZE}}')
            msg = bytes(f'{len(msg):<{HEADERSIZE}}', 'utf-8') + msg

            time = time + 0.1

            s.send(msg)
            print(f"Server: {msg}")
            
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da sind doch immer noch die gleichen Probleme drin. Jetzt benutzt du ploetzlich TCP, schreibst aber noch von UDP. Dann brauchst du auch ein Protokoll, dass du beim schreiben auch mit deinem headersize erzeugst. Aber du liest das nicht ein, sonder versuchst schon wieder direkt die gelesenen Daten zu deserialisieren. Natuerlich geht das nicht. Pickle hat ein wohldefiniertes Format, das nicht einfach mit beliebigen Bytes zu Beginn zurecht kommt.

Entweder du

- benutzt UDP
- schickst NUR pickle daten, keine HEADERSIZE-Gedoens
- deserialisiert diese Nachrichten mit pickle.

Oder du

- benutzt TCP
- benoetigst zwingend(!!!!!) ein Protokoll, wie zb eine Zeile mit der Angabe in Bytes, die dann folgend einzulesen sind. Gefolgt vom pickle dump.Und musst das auch befolgen, Zustandsmaschine und Verwendung von sendall inklusive.
- extrahierst dann den eigentlichen Datenblock aus einer eingehenden Kommunikation, und deserialisiertst den dann mit pickle.

Um das nochmal ganz deutlich zu sagen, falls die 5 Ausrufezeichen nicht deutlich genug waren: TCP ist NICHT nachrichtenorientiert! Das ist ein *STROM* von Bytes, der an einer *beliebigen* Stelle unterbrochen werden kann, und dann kommt ein naechstes Paket mit dem Rest. Darum *MUSS* man ueber TCP ein Protokoll fahren! Sonst geht es nicht!
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da mir das mit dem message header komisch vorkam, habe ich mal danach gesucht. Und das hier gefunden: https://pythonprogramming.net/pickle-ob ... -python-3/

Da macht er das doch richtig. Es ist ein bisschen konvolut, aber er liest den Header ein, und vor allem macht er das hier:

Code: Alles auswählen

            print(pickle.loads(full_msg[HEADERSIZE:]))
Er respektiert also genau den header, und schneidet den *vor* dem deserialisieren ab.

Warum folgst du dem Tutorial nicht in gaenze?
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

ok, ich muss mich korrigieren. Was der da veranstaltet ist natuerlich auch schon wieder grober Unfug, weil er die Daten immer in 16 Byte Bloecken einliest. Wehe da werden mal zwei Pakete schnell hintereinander verschickt, dann landen ersten 4 Bytes des zweiten Pakets im letzten Block des ersten, und das ganze Kartenhaus faellt zusammen, weil er auf immer und ewig mehr daten erwartet. Denn er prueft ja mit ==, ob die Nachricht in Gaenze empfangen wurde.

Da draussen ist wirklich fast nur Sch*isse zu finden, was Netzwerkprogrammierung angeht. Wenn es keinen Grund gibt, warum du das Rad neu erfinden willst, kann ich dir nur mit Nachdruck zu existierenden Bibliotheken wie ZeroMQ, nanoms odr nng (persoenliche Wahl), und HTTP servern raten.
Benutzeravatar
__blackjack__
User
Beiträge: 12984
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Was man auch machen könnte ist sich die TCP-Verbindung in ein Dateiobjekt zu verpacken, Socket-Objekte haben da eine Methode für (`makefile()`), und einfach `pickle.load()` verwenden, denn die Pickledaten selbst enthalten auch die nötigen Informationen über die Länge und `pickle.load()` hört auf zu lesen wenn die Daten von einem ”Pickle” komplett sind.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten