TCP String senden

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Quakxi
User
Beiträge: 7
Registriert: Donnerstag 19. März 2015, 22:42

Hallo,

ich will einen String senden, sollte eig. kein Problem sein, Visual Basi versteht allerdings den STring nicht (ich hiffe das ich irgendwas mit dem Stringformat verhauen habe, sonst muss ich mit Byte senden anfangen).

Von Python zu Python funtzt aber sonst nicht

Code: Alles auswählen

TCP_IP = '192.168.2.108'
TCP_PORT = 4000
BUFFER_SIZE = 1024

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

lieder = []

i=0
def WennVerbunden():
        liederUn = os.listdir("/media/usb/")
        for lied in liederUn:
                if lied[0] == ".":
                        i=1
                elif lied != "System Volume Information":
                        lieder.insert(len(lieder),lied)
                        print(lied)
        print("lieder werden gesendet")
        for lied in lieder:
                conn.send(str(lied))
                print(lied)
        conn.send("Ende")
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
print("Server ist bereit")
conn, addr = s.accept()
print 'Verbinundungsaufbau mit :', addr


while 1:
    data = conn.recv(BUFFER_SIZE)
    if not data: break
    else:
                print "Verbunden mit:",addr
                verbunden = True
                WennVerbunden()

conn.close()

DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Wir können nicht raten was dein Visual Basic Client falscht macht ohne eine Beschreibung des Fehlers und Code.
BlackJack

@Quakxi: Man kann über TCP nur Bytes verschicken und empfangen. Wenn man Zeichenketten hat, dann muss man die vorher als Bytes kodieren. Und beide Seiten sollten sich einig darüber sein *wie* die Zeichenketten kodiert sind.

Sonstige Anmerkungen zum Quelltext:

`lieder` sollte nicht auf Modulebene definiert werden. Das Hauptprogramm gehört dort auch nicht hin, sondern auch in eine Funktion. Sonst verliert man bei allem das über ein paar Zeilen hinaus geht den überblick weil alles irgendwie mit allem anderen zusammenhängen könnte, statt saubere, kleine, in sich geschlossene Funktionen zu verwenden.

`i` wird auf Modulebene definiert, aber nirgends benutzt. Das lokale `i` in der `WennVerbunden()`-Funktion hat mit dem anderen `i` nichts zu tun. Und das in der Funktion wird auch nur definiert wenn ein Dateiname mit einem Punkt anfängt. Und dann aber auch wieder nirgends benutzt. Weg damit.

Die Schreibweise von `WennVerbunden()` entspricht nicht dem Style Guide for Python Code, wie auch ein paar andere Sachen nicht. Ausserdem ist das kein guter Name für eine Funktion, die genau wie Methoden, normalerweise nach Tätigkeiten benannt werden, damit man weiss was sie tun. `send_filenames()` wäre zum Beispiel ein sprechenderer Bezeichner.

`liederUn` ist unverständlich. Was soll das `Un` bedeuten? Namen sollen dem Leser vermitteln was der Wert dahinter bedeutet und nicht zum rätselraten animieren.

`insert()` ist definitiv die falsche Methode um einen Wert an eine Liste *anzuhängen*. Wie kommt man auf diese Idee?

``print`` ist in Python 2 eine Anweisung und keine Funktion also sollte man das auch nicht wie einen Funktionsaufruf schreiben. Oder aus dem `__future__`-Modul den Namen `print_function` importierten damit es zu einer Funktion wird. Dann muss man aber auch wirklich alle Vorkommen als Funktionsaufruf schreiben.

Der `str()`-Aufruf mit `lied` ist überflüssig.

Die Dateinamen werden ohne jegliche Trennzeichen übertragen — wie soll der Empfänger denn das wieder auseinander dividieren?

Python hat Wahrheitswerte (`True` und `False`) die sollte man auch verwenden wenn man sie meint, und nicht 1 und 0.

Die Semantik von der Empfangsschleife sieht komisch aus. Wie oft die durchlaufen wird hängt von unverhersehbaren Faktoren ab, nämlich wie die ankommenden Daten übertragen und gepuffert werden, also wie viele `recv()`-Aufrufe letztendlich benötigt werden um die empfangenen Daten auszulesen. Der Inhalt wird auch komplett ignoriert.

`verbunden` wird definiert und nicht benutzt.

Dadurch das `lieder` auf Modulebene definiert ist, wird diese Liste bei jedem Aufruf von `WennVerbunden` länger. Das sieht mir auch falsch aus.
Quakxi
User
Beiträge: 7
Registriert: Donnerstag 19. März 2015, 22:42

@Dasich
ich versteh das Problem, der VB-Code ist allerdings richtig, da ich ihn mit nur geringfügigen Abweichungen benutzt habe.

@BlackJack
Danke für den Tipp mit den Bytes, ich weis das mein Code jetzt nicht der schönste ist, ist auch noch in Entwicklungsphase, aber dennoch Danke für die Tipps werde versuchen sie zu befolgen
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Quakxi: Der Code ist nicht nur unschön, er hat auch einige Fehler. Es werden definitiv Bytes über TCP gesendet. Was VisualBasic erwartet, kann man nur sagen, wenn man den Code dazu kennt. Was bedeutet, VB versteht den String nicht? Gibt es eine Fehlermeldung? Wie schon BlackJack geschrieben hat, werden immer nur Bytes übertragen. Wenn aber VB einen UCS16 codierten String erwartet, kann man den natürlich schon auf der Pythonseite so kodieren. Schöner wäre es allerdings, wenn man ein allgemein gebräuchliches Protokoll verwenden würde, dann wäre man nicht VB abhängig und könnte sich das erfinden eines eigenen Protokolls sparen.
BlackJack

Das ganze mal in fehlerfreier und mit einem Nullbyte als Abschluss jedes Dateinamens damit man die beim Empfänger auch wieder getrennt bekommt:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import os
import socket

ADDRESS = ('127.0.0.1', 4000)
PATH = '/media/usb'


def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(ADDRESS)
    sock.listen(1)
    print('Server wartet auf Verbindungen.  Beenden mit Strg+C.')
    try:
        while True:
            connection, address = sock.accept()
            try:
                print('Verbunden mit:', address)
                for name in os.listdir(PATH):
                    if (
                        not name.startswith('.')
                        and name != 'System Volume Information'
                    ):
                        print(name)
                        connection.sendall(name + '\0')
            finally:
                connection.close()
                print('Verbindung zu {0} beendet.'.format(address))
    except KeyboardInterrupt:
        print('Server beendet.')


if __name__ == '__main__':
    main()
Es werden Bytes gesendet, und zwar genau die, die auch vom Dateisystem geliefert werden. Zumindest unter Unix sind Dateinamen ja auch Bytes und keine Zeichenketten.
Quakxi
User
Beiträge: 7
Registriert: Donnerstag 19. März 2015, 22:42

Danke für die Verbesserung, im VB-Script kann ich leider nicht mit NullByte arbeiten, bzw. ich habs anders gemacht (mag ich an der Informatik es gibt unendlich viele Möglichkeiten) aber sehr nett, das Ihr euch die Zeit nehmt mir einen besseren "Stil" beizubringen/Tipps dafür gebt
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Quakxi hat geschrieben:Danke für die Verbesserung, im VB-Script kann ich leider nicht mit NullByte arbeiten, bzw. ich habs anders gemacht
Wie denn? Irgend wie musst Du ja ein "Ende" erkennen!
Quakxi hat geschrieben: (mag ich an der Informatik es gibt unendlich viele Möglichkeiten)
Die gibt es ja fast immer im Leben - aber wenn man etwas gegen den Trend macht, muss man schon eine gute Begründung haben oder mit den Schwierigkeiten klaglos leben :mrgreen:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Quakxi: Über TCP werden Bytes übertragen und 0 ist ein ganz normaler, gültiger Wert aus dem Wertebereich von Bytes. Wieso kannst Du damit in VB nicht arbeiten? Das würde ja bedeuten das man mit VB nicht mit Sockets arbeiten kann. Kann ich mir jetzt nicht wirklich vorstellen.
Quakxi
User
Beiträge: 7
Registriert: Donnerstag 19. März 2015, 22:42

Ich glaub ich habe mich falsch ausgedrückt:
Um mit VB Bytes aus einem Stream auszulesen, brauch ich ein bisschen mehr Code.
Bzw. die "normalen" bereits vorgegeben Methoden wie Read, ReadLine ect. arbeiten fast alle mit String als rückgabe Wert.
Um das Ende zu erkennen war das send("Ende") zuständig.

@Hyperion Ich bin ein Fan von "deutschen" Text in Code, ich rege zwar damit fast jeden auf der meine Code lesen will.
Ich sehs als Obfuscator an, kein nicht Deutscher kann somit meinen Code wirklich verstehen(bis auf Google Übersetzer oder sowas), bzw. ist es schwerer
:wink:
BlackJack

@Quakxi: Mal abgesehen davon das 'Ende' auch der Name einer Datei sein könnte, ist das keine Möglichkeit die *einzelnen* *Dateinamen* voneinander zu trennen. Das einzige was da, zumindest unter den meisten Dateisystemen, nicht drin vorkommen kann, ist ein Nullbyte. Und für das Trennen von einzelnen Namen ist 'Ende' noch weniger geeignet als es zum Kennzeichnen des Endes der Übertragung zu verwenden, denn 'Ende' kann ja auch *innerhalb* von Dateinamen als Zeichenkette auftauchen. Das Ende der gesamten Übertragung lässt sich durch das schliessen der Verbindung signalisieren, da braucht man keine Daten für senden.

Etwas mehr Code schreiben zu müssen sollte einen nicht davon abhalten Programme ohne Fehler zu schreiben.
Quakxi
User
Beiträge: 7
Registriert: Donnerstag 19. März 2015, 22:42

Praktisch jede Datei hat ein .xyz am Ende, es wäre zwar möglich das ganze auch mit einen "StoppCodon", das halt kein .xyz am Ende hat.
Ich habe das Problem nun meines Erachtens eleganter gelöst, in dem ich einfach alle lieder in eine Variable schreibe und sie mit einer Pipe| trenne, dann einfach eine Split Methode und das Problem ist gelöst (man könnte auch theoretisch ein "/" nehmen, das ist als Dateiname verboten)

Edit: Das Ende wird erst am Ende jedes gesendten Liednamens gesendet, die wiederum in einzelnen Pakete, per split bei .wav hab ich dann meinen Dateinamen(war meine erste Idee, wie gesagt, ist überarbeitet)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Quakxi:
Was Du da schreibst mit Stopcodon oder Zeichen xy kann/darf nicht vorkommen und kann daher als Trennzeichen verwendet werden sind Überlegungen in Richtung eines Protokolles, welches es Dir ermöglicht, Dateinamen getrennt zu versenden. Mein Tipp an dieser Stelle - das Rad nicht neu erfinden, denn solche Protokolle mit den passenden De-/Serialisierern gibt es schon für zig Sprachen, z.B. JSON. (http://en.wikipedia.org/wiki/JSON_Streaming)
Antworten