Länge gesendeter Daten?

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Wenn ich empfange mit:

Code: Alles auswählen

self.request.recv(32)
und wenn dann die Länge der Daten kleiner als 32 ist, dann müßte ich alles empfangen haben. Aber wenn die Länge der Daten gleich 32 ist, wie weiss ich dann, ob nur 32 Daten gesendet wurden oder ich noch nicht alles gelesen habe?

Sollte man am Anfang einer Message die Datenlänge übertragen, oder kann man das auch noch auf andere Art feststellen? Da ich ein Bytearrray haben will, scheidet so etwas wie EOL aus.
BlackJack

@Alfons Mittelmeyer: Wenn die Länge kleiner als 32 (aber mindestens 1) ist musst Du *nicht* alles empfangen haben. Du musst so oft `recv()` aufrufen bist Du sicher bist das Du alles empfangen hast und zwar entweder weil Du weisst wieviele Bytes kommen sollen oder weil Du das an den empfangenen Daten sehen kannst ob sie vollständig sind. Der Code muss so geschrieben sein das ``recv(32)`` im Extremfall bei jedem Aufruf nur 1 Byte liefert.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@BlackJack Danke, hatte ich mir fast gedacht, denn das ist ein Beispiel aus tinyrpc:

Code: Alles auswählen

def _read_n_bytes(sock, n):
    buf = []
    while n > 0:
        data = sock.recv(n)
        n -= len(data)
        buf.append(data)

    return ''.join(buf)
Muss ich dann auch so machen, nur dann mit fester Bufferlänge, will schließlich nicht einige MB und b''.join(buf)

Ach so, wie lange darf ein Array sein? Ich danke da an MP3 Dateien, Begrenzung auf 64 KB oder nicht?
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: Pufferlängen von einigen MB sind unproblematisch. Aber wenn Du MP3-Dateien übertragen willst, ist wohl ein Streaming-Protokoll besser geeignet als ein Block-Protokoll.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: Pufferlängen von einigen MB sind unproblematisch. Aber wenn Du MP3-Dateien übertragen willst, ist wohl ein Streaming-Protokoll besser geeignet als ein Block-Protokoll.
Das wäre eigentich mehr gedacht um PDF Dateien an mehrere Schüler zu übertragen, wollte nur wissen, welche Längen gehen. Ein Streaming Protokoll möchte ich freilich auch noch haben. Das werde ich dann aber nicht selber machen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Habe jetzt die Lesefunktion von readline umgestellt auf Byte-Array mit Längenangabe:

Code: Alles auswählen

    def read_message(self,socket):
        buf = []

        # get length
        n = 4
        while n > 0:
            data = socket.recv(n)
            if len(data) == 0: return None 
            n -= len(data)
            buf.append(data)

        n = int.from_bytes(b''.join(buf), byteorder='big')

        # get data
        while n > 0:
            if n > 1024: buflen = 1024
            else: buflen = n
            data = socket.recv(buflen)
            if len(data) == 0: return None 
            n -= len(data)
            buf.append(data)

        return b''.join(buf)
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Alfons: Dein Code ist problematisch. Stell dir vor, was wohl passiert, wenn du aus irgendwelchen Gründen beim erten `recv()` nur 3 Bytes empfängst? Dann holst du dir im nächsten Versuch wieder 4 Bytes und somit 3 Bytes zuviel. Abgesehen davon, dass es bei der nachfolgenden Operation für die Ermittlung der Länge dann ohnehin krachen würde, setzt sich der Fehler auch fort, da halt die ersten 3 Bytes des Contents versehentlich schon abgegriffen wurden. Somit würden deinem Content 3 Bytes vorne fehlen.
BlackJack

@Alfons Mittelmeyer: Ich würde ja immer noch die `makefile()`-Methode verwenden, dann muss man das nicht alles selbst neu erfinden und es ist ein bisschen weniger Code (ungetestet):

Code: Alles auswählen

def read_message(socket):
    in_file = socket.makefile('rb')
    return in_file.read(int.from_bytes(in_file.read(4), byteorder='big'))
@snafu: Wieso würden beim nächsten Versuch wieder 4 Bytes gelesen? `n` wird doch nach jedem `recv()`-Aufruf um die Anzahl der tatsächlich damit empfangenen Bytes verringert.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Stimmt. Habe den Code falsch gelesen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@BlackJack Dein Code würde mir auch besser gefallen, leider bekomme ich da folgenden Fehler: TypeError: cannot convert unicode object to bytes
Und wenn ich noch bytearray reinschalte, bekomme ich einen anderen Fehler: TypeError: string argument without an encoding
Das mag einfach nicht in Bytes lesen. Hab es mit bytes und 'utf-8' probiert, dann kommt das Programm zwar einmal darüber hinweg, setzt aber später wieder aus.
Am Besten man liest wirklich in Bytes ohne irgendwie utf-8, wenn es Bytes sind.

Das geht wohl in Python 2.x bei Python3.x will es einfach so nicht.

Komisch ist aber, dass vom File lesen in Bytes noch geht:

Code: Alles auswählen

fh = open("foto.jpg","rb")
myjpg = bytearray(fh.read())
fh.close()
BlackJack

@Alfons Mittelmeyer: Den Fehler kann ich nicht nachvollziehen. Ich bekomme da Bytes. Das Problem muss ausserhalb dieser Funktion liegen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@BlackJack Welche Python Version benutzt Du? Ich bekomme unicode! Aber Sinn ergibt unicode in diesem Zusammenhang wohl nicht viel. Und die Rückverwandlung von einem Unicode String in Bytes klappt nicht.
Und was ich dabei benutze ist ganz normal:

Code: Alles auswählen

        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect((self.ROUTERHOST, self.ROUTERPORT))
Sollte also wohl an der Python Version liegen. Ich benütze 3.2.4
BlackJack

@Alfons Mittelmeyer: Interessant ist weniger die Verbindung sondern ob Du beim `makefile()` auch sagst das Du Bytes haben möchtest.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Interessant ist weniger die Verbindung sondern ob Du beim `makefile()` auch sagst das Du Bytes haben möchtest.
Vielleicht hatte ich das gestern vergessen. Habe es heute probiert mit:

Code: Alles auswählen

def read_message(socket):
    in_file = socket.makefile('rb')
    number = in_file.read(4)
    return number + in_file.read(int.from_bytes(number, byteorder='big'))
Beim Client hatte es funktioniert. Aber beim Router fehlten dann Daten. Habe auch teilweise leer empfangen. Geht unter gewissen Bedingungen aber unter anderen auch nicht. Zumindest nicht beim socketserver.BaseRequestHandler in Verbindung mit ThreadingMixIn. Da bleibe ich dann am Besten bei meiner Routine
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Habe mir gestern QPython auf dem Handy installiert. Und dann Fileübertragung getestet. 60 MB in 40 Sekunden waren nicht übel. Habe es dann heute mit Streaming gemacht. Das heißt, ich habe nicht den ganzen File auf einmal übertragen, sondern in 1 MB Blöcken. Da hatte es dann nur mehr etwa 20 Sekunden gedauert, weil man da nach dem ersten Block schon anfangen kann zu speichern. Das Sendeprogramm ist relativ einfach. Hier wird ein File auf dem PC geöffnet und dann dem Handy mitgeteilt, dass es einen zum Schreiben öffnen soll:

Code: Alles auswählen

file_handle = open("music.mp3","rb")
client.send("OPEN","/storage/extSdCard/tcp/music.mp3")
Und dann braucht man nur noch eine read Routine, über die das Programm auf dem Handy weitere Blöcke lesen kann:

Code: Alles auswählen

def read_block(message):
    global file_handle
    global block_nr
    print("Block: ", block_nr)
    block_nr += 1
    block = file_handle.read(1000000)
    if len(block) != 0: client.send("BLOCK",block)
    else:
        file_handle.close()
        client.send("EOF")

client.do_receive("READ",read_block)
Und wenn das Programm auf dem Handy Blöcke zwischenbuffert, dann braucht es nach Schreiben eines Blockes nicht erst auf den nächsten warten.
Antworten