Datein über Netzwerk kopieren funktioniert fehlerhaft

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
l05443
User
Beiträge: 2
Registriert: Samstag 12. Februar 2005, 17:51

Hallo Leute,

ich bin dabei, ein Programm zu erstellen, welches Dateien über Netzwerk kopiert. Die Übermittlung geschieht blockweise (z.B. 1MB-Packete) und wird Mithilfe eines SHA-Codes, welcher vor dem Versand erstellt wurde, nach dem Eingang auf ihre Vollständigkeit hin überprüft.
Kopiere ich local funktioniert alles reibungslos, doch sobald ich auf einen anderen Rechner im Netz kopieren möchte, scheint er mit dem Empfang von "checksum" und "filecontent" durcheinanderzukommen und vermischt die beiden. Das passiert mir vor allem, wenn die Packetgröße in der verschickt wird ("blocksize") ansteigt (1KB funktioniert gut, 1MB gar nicht, die Datei als ein Packet verschickt funktioniert wieder einwandfrei).
Wäre toll, wenn jemand eine Idee hat, wo mein Denkfehler liegt.

Das Versendeprogram:

Code: Alles auswählen

import filecmp
import socket
import sha


blocksize = 1024
readfrom="file1"
HOST = '192.168.1.99'
PORT = 50007


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
fr=file(readfrom, "rb")
count_filecontent=0
count_checksum=0

# sending blocksize
s.send(str(blocksize))
s.recv(1)

while 1:
    # read file
    filecontent=fr.read(blocksize)
    
    # create checksum of content
    sha_check=sha.new(filecontent)
    checksum=sha_check.hexdigest()

    # sending data
    count_filecontent+=s.send(filecontent)
    s.recv(1)
    print "CHECKSUM:", checksum
    count_checksum+=s.send(checksum)
    s.recv(1)
    if len(filecontent) != blocksize:
        break
fr.close()
s.close()
print count_filecontent/1024, "KB  + ", count_checksum, "KB Data sent."

while 1:
    try:
        txt=input("Enter -> Ende")
        break
    except:
        break
Das Empfängerprogram:

Code: Alles auswählen

import filecmp
import socket
import sha


writeto="file2"
HOST = ''
PORT = 50007


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
fw=file(writeto, "wb")


# receiving blocksize
blocksize=int(conn.recv(32))
conn.send("1")

while 1:
    # receiving data
    filecontent=conn.recv(blocksize)
    conn.send("1")
    checksum=conn.recv(160)
    conn.send("1")
    print "CHECKSUM:", checksum

    # checking file integrity
    sha_check=sha.new(filecontent)
    if not checksum == sha_check.hexdigest():
        print "Fehler in der Uebertragung! -> Abbruch"
    
    # writing data to file
    fw.write(filecontent)
    if len(filecontent) != blocksize:
        break
fw.close()
s.close()

while 1:
    try:
        txt=input("Enter -> Ende")
        break
    except:
        break
BlackJack

Die Grössenangabe bei recv() ist nur ein Maximalwert, es kann durchaus sein, dass weniger gelesen wird. Das sieht man schon daran, dass Du am Anfang die Blockgrösse als String sendest und auf der anderen Seite 32 Bytes als Puffergrösse angibst. Der String ist keine 32 Zeichen lang, aber trotzdem läuft das Empfangsprogramm weiter ohne darauf zu warten, das wirklich 32 Bytes ankommen.

Du musst also beim Empfangen in einer Schleife lesen und erst aufhören wenn Du wirklich die Daten für einen kompletten Block zusammen hast.

Code: Alles auswählen

filecontent = ''
while len(filecontent) < blocksize:
    filecontent += conn.recv(blocksize - len(filecontent))
Das man das Problem erst bei gösseren Blockgrössen sieht, liegt daran, dass diese irgendwann die Grösse den Empfangspuffers des Betriebssystems überschreiten und somit nicht auf einmal übertragen werden können oder es einfach zeitlich nicht mehr passt wenn das empfangende Programm die Daten schneller liesst als sie über's Netz nachgeliefert werden.

Wie kommst Du übrigens auf die Puffergrössen für die Blockgrösse und die Prüfsumme? SHA hat 160 bits und als hexdigest 40 Zeichen.
l05443
User
Beiträge: 2
Registriert: Samstag 12. Februar 2005, 17:51

Danke für den Tip BlackJack, hab ich gar nicht dran gedacht :roll:.

Hast du noch irgendeine elegante Idee, wie ich den letzten Datenblock (< blocksize) rüberbringe, ohne dass sich der Rechner in der Endlosschleife aufhängt.

Die blocksize ist willkürlich gewählt; es kommt später noch eine AES oder Serpent Verschlüsselung hinzu. Der SHA ist natürlich nur 40 Zeichen lang habs mit den 160bits verwechselt, müsste also der Wert 40 reichen.
BlackJack

Ich habe mit Sockets bisher nur unter C und Java was gemacht und habe gerade bei den Python-Sockets die Methode makefile() entdeckt. Die scheint ein normales Python file Objekt zu liefern, also mit read() bzw. write(). Probier das mal aus, dann brauchst Du die Schleife nicht selbst zu schreiben.

Der letzte Block: Da wirst Du wohl jedesmal die Blockgrösse schicken müssen und beim letzten Block einen entsprechend kleineren Wert. Ich verstehe allerdings nicht so recht warum Du überhaupt den ganzen Aufwand treibst. TCP sorgt doch schon für eine korrekte Übertragung. Wenn Du sicher gehen willst, dann würde ich lieber eine Checksumme über die ganze Datei bilden und die am Schluss übertragen.
Antworten