Problem bei Senden von Dateien per TCP

  • VB.NET

Es gibt 5 Antworten in diesem Thema. Der letzte Beitrag () ist von Minilulatsch.

    Problem bei Senden von Dateien per TCP

    Hallo Leute,

    ich bastle im Moment ein kleines Programm, das Dateien per TCP verschicken soll. Dabei gibt es natürlich einen Server und einen Client. Der Server liest die Dateien blockweise ein (bei meinen Tests war es eine .mp3-Datei) und der Client setzt sie wieder zusammen.
    Aber ich denke ich zeige euch erstmal meinen Code zum Versenden und Empfangen der Datei und erläutere danach das Problem, dass dabei auftritt (Die Code-Ausschnitte in denen der Server bzw. der Client erstellt wird und die Verbindung hergestellt wird lasse ich mal weg, da das Problem nichts damit zutun hat)

    Sub zum Versenden der Datei:

    VB.NET-Quellcode

    1. Private Sub DateiVersenden(ByVal Pfad as String)
    2. Dim fstm As Stream = New FileStream(Pfad, FileMode.Open, FileAccess.Read)
    3. Dim Größe As Long = fstm.Length
    4. streamw.WriteLine(Größe) 'hier wird dem Client die Größe der mitgeteilt, wobei das aktuell beim Client noch nicht integriert ist (mehr dazu in der Sub zum Dateien Empfangen)
    5. streamw.Flush
    6. Dim I As Integer = 0 'dieser Int-Wert zählt die Anzahl der Bytes, die bei jedem Block versendet wird
    7. Dim Gesamt As Long = 0 'soll mitzählen wieviele Bytes insgesamt verschickt wurden
    8. Dim Puffer(1024) As Byte 'der Puffer in dem die eingelesen Bytes gespeichert werden
    9. Do
    10. I = fstm.Read(Puffer, 0, Puffer.Length) 'die Größe des aktuellen Blocks wird gespeichert und die entsprechenden Bytes in den Puffer eingelesen
    11. If I < Puffer.Length Then 'sollte die Anzahl der Bytes im Block kleiner sein als die normale Puffergröße, wird hier mitgeteilt, dass die entsprechende Anzahl an Bytes die in diesem Block enthalten sind verschickt werden (das habe ich eingerichtet, da der letzte Block der einzulesenden Datei in der Regel ja kleiner ist als die üblichen Blöcke)
    12. stream.Write(Puffer, 0, I)
    13. Gesamt = Gesamt + (I) 'hier werden die verschickten Bytes mitgezählt
    14. Exit Do 'die Do-Schleife wird dementsprechend verlassen, da, wie oben schon angesprochen, hier der letzte Block eingelesen und verschickt wurde
    15. End If
    16. stream.Write(Puffer, 0, Puffer.Length) 'sollte es sich bei einem Block nicht um den letzten handeln, wird ganz normal der Inhalt des gesamten Puffers verschickt
    17. Gesamt = Gesamt + (Puffer.Length) 'hier werden die verschickten Bytes mitgezählt
    18. Loop
    19. MsgBox(Prompt:=Gesamt) 'diese MessageBox habe ich zur Kontrolle eingebaut, damit mir mitgeteilt wird, wieviele Bytes insgesamt verschickt wurden
    20. fstm.Close()
    21. MsgBox(Prompt:="Abgeschlossen")
    22. End Sub


    Sub zum Empfangen der Datei:

    VB.NET-Quellcode

    1. Private Sub DateiEmpfangen()
    2. Dim writer As New BinaryWriter(File.Open(Pfad, FileMode.Create))
    3. Dim I As Integer = 0 'hier wird die Anzahl der Bytes im eintreffenden Block gezählt
    4. Dim Größe As Long = streamr.ReadLine 'hier kommt die Größe der Datei an,damit der Client weiß wie lang er Bytes empfangen soll...Spielt allerdings bei diesem Code keine Rolle, da im Moment noch zu Testzwecken die Bytes in einer Do-Schleife empfangen werden.Genau wie oben wird hier außerdem der Puffer definiert in dem die Bytes gespeichert werden
    5. Dim Puffer(1024) As Byte
    6. Do 'wie bereits gesagt, eine Do-Schleife in der die Bytes empfangen werden. Ganz einfach deswegen damit beim Testen keine Schleife benutzt wird, die evtl. zu früh beendet wird, also um sicherzugehen, dass auch wirklich alle Bytes empfangen werden. Wird später dann durch eine Schleife ersetzt, die genau soviel einlist wie nötig
    7. I = stream.Read(Puffer, 0, Puffer.Length) 'hier werden die Bytes eingelesen
    8. writer.Write(Puffer, 0, I) 'hier werden sie in die Datei geschrieben
    9. Loop
    10. End Sub


    Um grad nochmal zur Do-Schleife beim Empfangen zu kommen, die versucht natürlich selbst wenn die Datei schon übertragen wurde noch fleißig weiter Daten einzulesen, obwohl vom Client schon nichts mehr gesendet wird. Da ich am Server erkennen kann wann die Datei vollständig verschickt wurde, beende ich den Client dann immer gewaltsam.

    Aber nun zum eigentlich Problem. Der eigentliche Transfer der Dateien funktioniert gut, die .mp3-Datei die ich zum Testen benutzt habe ist hinterher auch wiedergabefähig. Man sollte meinen, dass das ein gutes Zeichen ist, aber irgendwas läuft da trotzdem falsch. Die Beispiel-Datei war 6.445.231 Bytes groß. Die Datei die durch den Transfer erstellt wurde ist allerdings nur 6.443.008 Bytes, es fehlen also 2223 Bytes. Die MessageBox die ich zur Kontrolle beim Versenden benutzt habe, hat allerdings die vollständige Anzahl von 6.445.231 Bytes mitgezählt. Wie kann es also sein, dass 2223 Bytes anscheinend einfach verloren gehen? Die verlorenen Bytes führen zum einen dazu, dass die Dateien nachdem Transfer nicht mehr genutzt werden können, außer Mp3s, die können diese Verluste anscheinend verkraften. Außerdem macht es mir das unmöglich beim Empfangen eine Schleife zu erstellen, die beendet wird, sobald alle Bytes angekommen sind, da in keinem Fall die Anzahl der Bytes ankommen, die der Schleife mitgeteilt wird.

    Habt ihr eine Idee, wie es zu diesem Verlust kommt? Ich selbst kann im Code keine Ursache dafür finden und befürchte sogar, dass die Ursache gar nicht im Code liegt. Aber vielleicht gibt es ja dennoch eine Lösung.
    Ich hoffe ich hab alles so erläutert, dass ihr es theoretisch nachvollziehen könnt, wenn irgendwer es sogar praktisch nachvollziehen kann würde das bei der Ursachenfindung natürlich extrem helfen!

    Schonmal vielen Dank für eure Hilfe im Voraus!

    MfG

    Minilulatsch

    [EDIT]: Sorry, dass der Quelltext so chaotisch aussieht, ich hab mir bei den Kommentaren die ganze Formatierung zerschossen. Ich versuche mal noch da Ordnung reinzubekommen

    Dieser Beitrag wurde bereits 30 mal editiert, zuletzt von „Minilulatsch“ ()

    Ich hab gerade nochmal ein paar Dateien ausprobiert, das Probleme trat bei allen Dateien auf, sowohl .docx, .jpg, .exe und .mp3. Bis auf die .mp3 sind keine der Dateien danach noch ausführbar, alle werfen die Fehlermeldung aus, dass sie beschädigt sind. Ich hab gerade auch mal nach Gemeinsamkeiten beim Datenverlust geschaut, die Dateien die ich ausprobiert hab sind unterschiedlich groß um haben beim Versenden eine unterschiedliche Anzhal von Bytes verloren, es sind also nicht immer 2223 Bytes die verloren gehen. Es aber auch relativ zur Größe gesehen immer ein unterschiedlicher Anteil. Aber bei einer Datei verändert sich die Anzahl der verlorenen Bytes nie.

    MfG

    Minilulatsch
    Was vielleicht auch noch erwähnenswert ist, ist dass es keine Probleme
    gibt, wenn ich diese Funtionen lokal ausführe, sprich es findet kein
    Austausch über einen NetworkStream statt, sondern die Byte-Blöcke werden
    in ein und dem selben Programm eingelesen und direkt über einen
    BinaryWriter in die neue Datei geschrieben. Die Funktion würde also dann
    so aussehen:

    VB.NET-Quellcode

    1. Dim fstm As Stream = New FileStream(Pfad, FileMode.Open, FileAccess.Read)
    2. Dim writer As New BinaryWriter(File.Open(Pfad_2, FileMode.Create))
    3. Dim I As Integer = 0
    4. Dim Gesamt As Long = 0
    5. Dim Puffer(1024) As Byte
    6. Do
    7. I = fstm.Read(Puffer, 0, Puffer.Length)
    8. If I < Puffer.Length Then
    9. writer.Write(Puffer, 0, I)
    10. Gesamt = Gesamt + (I)
    11. Exit Do
    12. End If
    13. writer.Write(Puffer, 0, Puffer.Length)
    14. Gesamt = Gesamt + (Puffer.Length)
    15. Loop
    16. fstm.Close()
    17. writer.Close()
    18. MsgBox(Prompt:="Abgeschlossen")
    19. End Sub


    Hier geht kein einziger Byte verloren. Es muss also tatsächlichso sein, dass durch den NetworkStream Verluste entstehen. Ich kann mir aber einfach nicht erklären, warum das so ist.

    Minilulatsch schrieb:

    Dim Größe As Long = streamr.ReadLine
    Das klaut dir doch schon mal den Anfang der Datei.
    Lass das mal weg.
    Bei einer MP3 macht das evtl. (je nach ID3-Version) beim Abspielen nichts aus, aber ein paar Tags werden schon fehlen.

    Außerdem würde ich nie mit Umlauten und sonstigen Non-ASCII-Zeichen im Code arbeiten, aber das ist deine Sache.
    Falls du in deinem Leben je mal in einer internationalen Umgebung arbeiten wirst, werden sie dir das aber ganz schnell abgewöhnen.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „petaod“ ()

    Habs grad mal ausprobiert, macht aber keinen Unterschied. Hätte mich aber auch gewundert, da diese streamr.readline zeile ja keine vom Server gesendeten Bytes einliest, da der Server vorm Versenden der Bytes ja noch eine streamw.writeline Anweisung hat, bei der die Größe verschickt wird. Dementsprechend liest die ReadLine Anweisung die Größe ein, Bytes sollten da keine drin landen, die beim Zusammensetzen der einzelnen Blöcke dann fehlen würden. Oder meintest, dass diese Zeile aus anderen Gründen Probleme machen sollte? Aber wie gesagt, es ändert sich nichts.

    Und mit dem Umlauten hast du natürlich recht, eigentlich ist das sehr unschön, das sagen mir auch meine Bücher. Das werd ich mir aber irgendwann schon wieder abgewöhnen.

    Danke für deine Antwort!

    MfG

    Minilulatsch