Moin zusammen!
Ich bin noch relativ neu bei Python und vollkommen neu in diesem Forum und hoffe deshalb, dass man mir meine Anfänger-Fragen nachsieht. Ich beschäftige mich gerade mit den Grundlagen der Netzwerkkommunikation unter Python und habe einen ersten kleinen Serverdienst geschrieben, der mir einen rudimentären Zugang zum Betriebssystem bietet und Befehle wie z.B. 'ls' ausführt. Nicht spektakulär, aber ich will ja auch erst einmal verstehen. Bei der Programmierung des Clients stellen sich mir nun zwei Fragen:
(1) Wenn ich Antworten des Servers mit socket.recv() entgegennehme, muss ich dieser Methode zwingend eine Puffergröße übergeben. In der Python-Refernz heißt es dazu: "For best match with hardware and network realities, the value of bufsize should be a relatively small power of 2, for example, 4096." Woher weiß ich aber nun genau, welchen Wert ich da brauche?
(2) Verschiedene Betriebssystem-Befehle wie 'ls' oder 'pwd' erzeugen unterschiedliche Ausgaben. Aus der Sicht des Clients weiß ich vorher nicht, was da kommt. Bei 'pwd' kommt eine einzige Zeile, die mein Client auch vernünftig darstellt. Bei 'ls' hingegen erzeugt der Server zunächst eine Liste, die dann zeilenweise an den Client geschickt wird. Da klappt der Empfang nicht so, wie ich es mir erhoffe - einige Zeilen werden angezeigt, andere erst dann, wenn ich clientseitig erneut die Enter-Taste drücke. Danach ist mein Client allerdings "durcheinander", und ich auch.
Vielleicht kann mich jemand erhellen. Vielen Dank und freundliche Grüße
fauxxami
Newbie-Fragen zum Datenempfang
@fauxxami: die Puffergröße ist relativ egal, weil es kommen eh die Bytes, die gerade anliegen. Also eine sehr große Puffergröße würde nie genutzt werden, bei einer zu kleinen Puffergröße ist der einzige Nachteil, dass es etwas langsamer wird, weil viele Systemaufrufe stattfinden.
Zu (2): Du brauchst ein Protokoll, an dem Du erkennst, wann eine Antwort fertig ist. Also im einfachsten Fall ein Header mit Länge in Bytes und falls kein weiterer Block mehr kommt, dann kann die Länge ja 0 sein.
Um konkret zu sagen, was Dein Code falsch macht, brauchen wir den Code.
Zu (2): Du brauchst ein Protokoll, an dem Du erkennst, wann eine Antwort fertig ist. Also im einfachsten Fall ein Header mit Länge in Bytes und falls kein weiterer Block mehr kommt, dann kann die Länge ja 0 sein.
Um konkret zu sagen, was Dein Code falsch macht, brauchen wir den Code.
Vielen Dank für die Antworten:
Grundsätzlich allerdings wüsste ich gerne, ob ich auf der einen Seite zu jedem Aufruf von socket.send() einen entsprechenden Aufruf von socket.recv() brauche. Denn dann würde mir deine Aussage zum "Protokoll" sinnvoll erscheinen, weil ich ja sonst auf der Client-Seite gar nicht weiß, wie häufig ich socket.recv() aufrufen muss. Oder?
Ok, das habe ich verstanden. Also kommen die Pakete höchstens verzögert, gehen aber nicht verloren. Ist auch eigentlich klar, ist ja TCP.die Puffergröße ist relativ egal, weil es kommen eh die Bytes, die gerade anliegen.
Ich glaube, mir ist gerade selber eine Lösung eingefallen. Bisher habe ich die Systemaufrufe mit dem Modul os realisiert, aber gerade gelesen, dass man das heute eigentlich besser mit subprocess macht. Und subprocess.check_output() liefert ja immer einen Byte-String, den ich so, wie er ist, über das Socket versenden kann - dann muss sich der Client nur noch darum kümmern, was drin ist. Aber dann weiß man jedenfalls, dass immer ein solcher Byte-String kommt.Du brauchst ein Protokoll, an dem Du erkennst, wann eine Antwort fertig ist. Also im einfachsten Fall ein Header mit Länge in Bytes und falls kein weiterer Block mehr kommt, dann kann die Länge ja 0 sein.
Grundsätzlich allerdings wüsste ich gerne, ob ich auf der einen Seite zu jedem Aufruf von socket.send() einen entsprechenden Aufruf von socket.recv() brauche. Denn dann würde mir deine Aussage zum "Protokoll" sinnvoll erscheinen, weil ich ja sonst auf der Client-Seite gar nicht weiß, wie häufig ich socket.recv() aufrufen muss. Oder?
Poste ich gerne, sobald mir jemand etwas zu den vorstehenden Fragen geschrieben hat. Noch habe ich den Ehrgeiz, es "alleine" zu schaffen.Um konkret zu sagen, was Dein Code falsch macht, brauchen wir den Code.
Hmmm, da das nun doch alles nicht so klappt, wie es soll, schicke ich doch schon mal den Client-Code:
Code: Alles auswählen
import socket
# Socket für IPv4 und TCP erzeugen:
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Socket mit dem Server-Socket verbinden:
clientSocket.connect(("server.local", 10000))
# Kommunikation mit dem Server beginnen:
try:
# Begrüßungsmeldung des Servers empfangen und ausgeben:
antwort = clientSocket.recv(1024)
print(antwort.decode())
# Endlosschleife für die Befehlseingabe:
while(True):
# Befehl eingeben und absenden:
befehl = input("Anweisung: ")
clientSocket.send(befehl.encode())
# Bei Eingabe von 'bye' soll die Verbindung
# beendet werden; Endlosschleife verlassen:
if befehl == "bye":
break
else:
# Alle anderen Anweisungen erzeugen einen Byte-String;
# dieser wird empfangen und ausgegeben:
antwort = clientSocket.recv(4096)
print(antwort.decode())
finally:
# Das Socket wird geschlossen:
clientSocket.close()
- __blackjack__
- User
- Beiträge: 13241
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@fauxxami: TCP ist nicht paket- oder nachrichtenorientiert. `send()` und `recv()` gehören nicht 1:1 zusammen. `send()` muss auch gar nicht alles senden was man da übergibt! Du willst da `sendall()` verwenden. Und `recv()` kann zwischen einem Byte und der Anzahl die man da als Argument angegeben hat alles liefern. Im Extremfall muss man damit klar kommen können, das `recv()` immer nur ein einzelnes Byte liefert bei jedem Aufruf. Oder umgekehrt das bei einem `recv()`-Aufruf auch Daten kommen können die mehr als eine Nachricht ganz oder teilwesie enthalten können. Darum braucht man ein Protokoll, also eine Möglichkeit Nachrichtengrenzen zu erkennen.
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
"Etwas langsamer" kann hier durchaus 10x langsamer sein, wenn IO das Bottleneck ist und du viele Daten hast. Praktischerweise gibt es auf sockets eine `makefile` Methode, die ein Objekt zurück gibt dass sich um Buffering kümmert. Dann kann man soviel Abfragen wie es gerade praktisch ist ohne die Konsequenz jedesmal einen Systemaufruf zu machen.Sirius3 hat geschrieben: ↑Freitag 16. Oktober 2020, 13:55 @fauxxami: die Puffergröße ist relativ egal, weil es kommen eh die Bytes, die gerade anliegen. Also eine sehr große Puffergröße würde nie genutzt werden, bei einer zu kleinen Puffergröße ist der einzige Nachteil, dass es etwas langsamer wird, weil viele Systemaufrufe stattfinden.
Ups, langsam, ich komme nicht mehr mit:
Nicht paketorientiert? Das wäre mir jetzt neu und widerspricht allem, was ich bisher so gelernt habe. Wikipedia meint z.B.: "Das Protokoll ist ein zuverlässiges, verbindungsorientiertes, paketvermitteltes Transportprotokoll in Computernetzwerken." Aber wie auch immer: ich habe noch nicht verstanden, wo der Zusammenhang mit meinem Problem ist.TCP ist nicht paket- oder nachrichtenorientiert.
Jetzt wird es doch spannend. Ich dachte: ich konstruiere auf der einen Seite ein Byte-Objekt, verschicke es per send(), und dann empfange ich das genauso auf der anderen Seite mit revc(). Das ist wohl falsch, ich habe eben noch mal die Referenz dazu gelesen. An der Stelle ist Java dann wohl doch leichter zu verstehen.`send()` und `recv()` gehören nicht 1:1 zusammen. `send()` muss auch gar nicht alles senden was man da übergibt!
Ok. Wenn ich das will, dann will ich das.Du willst da `sendall()` verwenden.
Verstehe. Und dieses Protokoll denke ich mir selbst aus? Die sendende Instanz ermittelt z.B. die Größe des Byte-Objekts, teilt sie dem Empfänger mit und der prüft dann, ob er alles bekommen hat? Und wenn nicht, sagt er: schicke den Kram noch mal?Darum braucht man ein Protokoll, also eine Möglichkeit Nachrichtengrenzen zu erkennen.
Was spricht denn eigentlich gegen HTTP? Dann muss man sich um viele Detailfragen bezüglich der reinen Übertragung nicht mehr kümmern. Gerade wenn, wie hier, die Kenntnisse zum OSI-Modell eher begrenzt sind, würde ich fast immer zur höchstmöglichen Schicht raten...
Die Beschreibung als nicht Paketorientiert bezieht sich auf die Abstraktion die du benutzt. Das socket interface. Das garantiert nicht, das ein Datenpaket das du abgeschickt hast als Ganzes auch so ungeteilt ankommt. Sondern nur einen beliebigen Strom von Bytes, die keine Abgrenzung haben. Das darunter das Protokoll Pakete nutzt, ist dafür genauso irrelevant wie die Frage ob Licht, Elektronen oder elektromagnetische Wellen als Medium genutzt werden.
Und darum braucht es ein Protokoll. Das kann ganz einfach zeilenbasiert sein. Oder mit einem fixen Header und einer längenangabe wie HTTP. Und diverse andere Spielformen.
Was du nicht machen musst ist bei einer stabilen (!) Verbindung gegenprüfen, ob alles angekommen ist. Das garantiert TCP, oder du bekommst einen Fehler. Was du dann machst hängt aber im Grunde über diesem Protokoll. Zb neu Anfragen. Oder einfach das nächste Datum senden.
Und darum braucht es ein Protokoll. Das kann ganz einfach zeilenbasiert sein. Oder mit einem fixen Header und einer längenangabe wie HTTP. Und diverse andere Spielformen.
Was du nicht machen musst ist bei einer stabilen (!) Verbindung gegenprüfen, ob alles angekommen ist. Das garantiert TCP, oder du bekommst einen Fehler. Was du dann machst hängt aber im Grunde über diesem Protokoll. Zb neu Anfragen. Oder einfach das nächste Datum senden.
- __blackjack__
- User
- Beiträge: 13241
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@fauxxami: Okay, wir müssen hier auseinanderhalten wie TCP selbst arbeitet und was Du als Programmierer als API bekommst. An dem Wort „paketvermitteltes“ ist auf Wikipedia ja eine Fussnote mit dem Inhalt:
„Nicht zu verwechseln mit paketvermittelnd. Die Aufgabe von TCP ist es nicht, Pakete zu übertragen, sondern die Bytes eines Datenstroms. Die Paketvermittlung wird durch das Internetprotokoll (IP) bereitgestellt. Daher ist IP paketvermittelnd aber TCP paketvermittelt.“
Ist also kein Widerspruch zu dem was ich gesagt habe sondern sagt genau das gleiche.
Wenn eine Programmiersprache Socketprogrammierung einfacher macht, dann bastelt sie da noch was auf die BSD-Socket-API drauf, die letztlich heutzutage immer darunter liegt. Ist in Python letztlich nicht so viel anders, denn man kann sich ja vom Socket-Objekt ein Dateiobjekt geben lassen, das die üblichen `read()`- und `write()`-Methoden hat.
Das Protokoll denkst Du Dir selbst aus. Aber eigentlich nur wenn man langeweile hat, denn es gibt ja schon so viele Protokolle da draussen, mit Implementierungen.
Grösse von Nachrichten als erstes schicken ist eine von mehreren Varianten die möglich sind.
Der Empfänger bekommt immer alles was gesendet wurde, in der Reihenfolge in der es gesendet wurde. Dafür sorgt TCP schon.
„Nicht zu verwechseln mit paketvermittelnd. Die Aufgabe von TCP ist es nicht, Pakete zu übertragen, sondern die Bytes eines Datenstroms. Die Paketvermittlung wird durch das Internetprotokoll (IP) bereitgestellt. Daher ist IP paketvermittelnd aber TCP paketvermittelt.“
Ist also kein Widerspruch zu dem was ich gesagt habe sondern sagt genau das gleiche.
Wenn eine Programmiersprache Socketprogrammierung einfacher macht, dann bastelt sie da noch was auf die BSD-Socket-API drauf, die letztlich heutzutage immer darunter liegt. Ist in Python letztlich nicht so viel anders, denn man kann sich ja vom Socket-Objekt ein Dateiobjekt geben lassen, das die üblichen `read()`- und `write()`-Methoden hat.
Das Protokoll denkst Du Dir selbst aus. Aber eigentlich nur wenn man langeweile hat, denn es gibt ja schon so viele Protokolle da draussen, mit Implementierungen.
Grösse von Nachrichten als erstes schicken ist eine von mehreren Varianten die möglich sind.
Der Empfänger bekommt immer alles was gesendet wurde, in der Reihenfolge in der es gesendet wurde. Dafür sorgt TCP schon.
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
@fauxxami: wenn Du an recv nichts geändert hast, dann läuft das nicht ganz prima, sondern einfach nur durch Zufall nicht katastrophal. Das Problem ist, dass solche Tests meist mit dem selben System unter idealen Bedingungen gemacht werden: keine Paketverluste, keine zwischengeschalteten Router, keine Zeitverzögerungen, ...
@fauxxami: Dann probier doch mal aus, was passiert, wenn deine "Willkommensnachricht" länger als 1024 Byte ist oder, wenn du nach 5 Byte einfach mal eine Minute nichts beim Sender.
Der Empfänger kann in deinem Programm nicht wissen, ob die Nachricht vollständig ist. "Funktioniert ganz prima" bedeutet in diesem Fall wirklich nur "Funktioniert, wenn man gerade Glück hat", und das ist doch nicht der Anspruch, den man hat - egal ob es ein Anfangsprogramm ist oder nicht.
Also entweder überlegst du dir ein Protokoll - was als Übung nicht schlecht ist - oder du musst etwas Eingebautes finden, was dir hilft.
Der Empfänger kann in deinem Programm nicht wissen, ob die Nachricht vollständig ist. "Funktioniert ganz prima" bedeutet in diesem Fall wirklich nur "Funktioniert, wenn man gerade Glück hat", und das ist doch nicht der Anspruch, den man hat - egal ob es ein Anfangsprogramm ist oder nicht.
Also entweder überlegst du dir ein Protokoll - was als Übung nicht schlecht ist - oder du musst etwas Eingebautes finden, was dir hilft.
Das ist völlig richtig - und deshalb bleibt es ja auch nicht bei der ersten Variante. Ich bin ja froh, mich ans Forum gewandt zu haben, weil mir eure Beiträge gezeigt haben, wo die Probleme liegen."Funktioniert ganz prima" bedeutet in diesem Fall wirklich nur "Funktioniert, wenn man gerade Glück hat", und das ist doch nicht der Anspruch, den man hat - egal ob es ein Anfangsprogramm ist oder nicht.
Was Eingebautes ist langweilig - ich überlege mir was Eigenes.Also entweder überlegst du dir ein Protokoll - was als Übung nicht schlecht ist - oder du musst etwas Eingebautes finden, was dir hilft.