String aus recv mit if vergleichen

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
TobiasM
User
Beiträge: 3
Registriert: Freitag 16. Mai 2014, 22:27

Hallo zusammen :)

Ich verzweifle hier gerade an einer eigentlich ganz simplen Sache (denn ich finde darauf auch bei Google einfach keine Antwort (auch nicht die Frage), sprich ich muss wohl einen riesen Denkfehler machen):

Auf nem Raspberry mit Raspbian hab ich ein Python 2.7.3. Darauf lasse ich das ganz simple Python Server Beispiel von https://docs.python.org/release/2.5.2/l ... ample.html laufen. Funktioniert auch alles bestens. Nur möchte ich nun gerne auf den Inhalt der Daten reagieren:

Im Server an der Stelle:

Code: Alles auswählen

while 1:
    data = conn.recv(1024)
    if not data: break
    conn.send(data)
conn.close()
Dort hab ich mir gedacht, bei Python 2.x ist data ja noch ein string, dann kannste ja einfach vergleichen:

Code: Alles auswählen

while 1:
    data = conn.recv(1024)
    if not data: break
    if data == "test": print "test bestanden"
    else: print "test nicht bestanden"
    conn.send(data)
conn.close()
Wenn ich nun an den Server "test" schicke, dann gibt er mir auch schön den string "test" wieder an den client zurück. Aber leider geht er immer in den else Zweig "test nicht bestanden". Die Bedingung wird also nicht erfüllt.
Was mache ich falsch?

Danke für jede Hilfe, ich komme mir ziemlich bescheuert vor :)
BlackJack

@TobiasM: Zwei Dinge: 1. Sendest Du nicht 'test' sondern etwas mehr, auf das Du dann auch testen musst. Lass Dir `data` dazu mal beim Server mit Hilfe der `repr()`-Funktion ausgeben, dann siehst Du was Dir beim Vergleich fehlt.

2. Das mit dem `recv()` funktioniert so nicht robust, denn der Aufruf liefert 1 bis 1024 Bytes vom Anfang des Datenstroms. Das heisst es kann auch sein das der Aufruf beim ersten mal nur ein 't' liefert, beim nächsten Aufruf vielleicht 'te' und so weiter. Du musst also sicherstellen das Du erst mindestens eine komplette Nachricht empfangen hast, bevor Du etwas mit den Daten anstellst. Umgehrt musst aber auch damit klarkommen, dass so ein `recv()` mehr als eine Nachricht zurückgeben kann.
TobiasM
User
Beiträge: 3
Registriert: Freitag 16. Mai 2014, 22:27

Besten dank für die Antwort, hat mir sehr geholfen :)

Bekomme also je nach OS entweder "test\n" oder "test\r\n". Habe also nicht an die Zeilenumbrüche gedacht :oops:

Zu 2)
Ich habe die Kontrolle darüber, was an den Server gesendet wird. Wollte nur Zahlencodes von 00-99 senden und dies über Java mit PrintWriter.println(). Da hatte ich bisher bei jedem Versuch die kompletten Daten bekommen. Nehme an, es liegt daran, dass mit println eine komplett abgeschlossene Zeile gesendet wird. Oder habe ich da was übersehen? :)
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@TobiasM: Du hast Glück, dass die Verbindung zwischen Server und Client relativ übersichtlich ist, das heißt es gibt keine Middle-Boxen, die den Datenstrom schlimm verändern. Garantieren kann man das im Allgemeinen nicht. Das Problem liegt also nicht an den beiden Programmen am Ende der Verbindung, sondern an den Stellen dazwischen, auf die Du keinen Einfluß hast. Dass die Probleme nur sehr selten auftreten ist kein Argument, es trotzdem falsch zu machen.
BlackJack

@TobiasM: Ad 2) Du übersiehst dabei das scheinbar funktionierender Code trotzdem falsch sein kann, eben weil einfach noch kein Fall aufgetreten ist bei dem der Fehler Auswirkungen hat. Es werden bei der BSD-Socket-API weder beim senden per `send()` noch beim Empfangen per `recv()` Garantien darüber gemacht was alles übertragen/empfangen wurde. Im Extremfall muss fehlerfreier Code damit klarkommen das bei jedem `send()` nur ein Byte übernommen wurde, man die Funktion also für jedes Byte aufrufen muss, und auf der anderen Seite kann es auch sein, dass man `recv()` für jedes Byte aus dem Datenstrom aufrufen muss. Wenn Du auf der einen Seite einmal '42\n' und danach '23\n' in den TCP-Datenstrom steckst, dann *kann* das mit zwei `recv()`-Aufrufen auslesbar sein, die genau diese beiden Teilstücke enthalten. Wahrscheinlich wird das auch oft so sein, zumindest wenn der zeitliche Abstand beim Senden gross genug ist und das Netzwerk nicht belastet ist. Aber halt nicht immer. Es kann auch sein, dass man beides in einem Aufruf bekommt '42\n23\n'. Es kann auch sein das man drei Stücke bekommt. '42', '\n2', '3\n', oder '4', '\n23', '\n', oder irgend eine andere Möglichkeit das auf Teilsequenzen runterzubrechen.

Beim Sender übernimmt die Java-API die Du da letztendlich benutzt die Arbeit ab `send()` oft genug aufzurufen oder `sendall()` zu verwenden. Aber auf Python-Seite erzeugst Du Socket-Objekte die nur eine ganz dünne Schicht über die BSD-Socket-API sind.

Wenn Du sicher bist, dass der Python-Code nur unter Unix/Linux-ähnlichen Systemen läuft, dann könntest Du Dir das leben vereinfachen in dem Du Dir vom Socket-Objekt mit der `makefile()`-Methode ein Dateiobjekt geben lässt. Das hat dann die üblichen Methoden die auch ein `file`-Objekt in Python hat, also zum Beispiel ist es iterierbar und liefert dabei die jeweils nächste Zeile aus dem Datenstrom. Dann kannst Du mit der `next()`-Funktion die nächste Zeile abfragen, oder noch schöner: man kann mit einer ``for``-Schleife über die Zeilen iterieren die über diese TCP-Verbindung herein kommen. Nachteil: die `makefile()`-Methode gibt es nicht unter Windows.
TobiasM
User
Beiträge: 3
Registriert: Freitag 16. Mai 2014, 22:27

Danke für die weiteren Antworten.
Da der Code nur unter Linux läuft, werde ich mir das Leben mit makefile() erleichtern :) Übrigens würde sowas wie 42 und anschließend 23 nicht vorkommen, da immer eine Verbindung aufgebaut, eine Zeile gesendet und die Verbindung direkt wieder geschlossen wird (ist nur ne Fernbedienungsfunktion fürs Smartphone).
Antworten