TCP-Filetransfer Probleme

  • VB.NET

Es gibt 3 Antworten in diesem Thema. Der letzte Beitrag () ist von ch3m.

    TCP-Filetransfer Probleme

    Hallo zusammen,

    bin ganz neu hier im Forum und sonst auch eher C-Milieu unterwegs.

    Ich hatte den Plan, mir in VB .NET eine P2P Anwendung zu schreiben, mit der ich problemlos & unkompliziert Dateien versenden bzw. empfangen kann.

    Das hat nach meiner Vorstellung auch funktioniert - bis auf das (bekannte) Problem, dass der Puffer beim Networkstream oft nicht ganz gefüllt wird, jedoch beim Empfänger immer ganz in die Datei geschrieben wird. Deshalb habe ich auf Infinity's Code zurückgegriffen, der dieses Problem mit den Methoden Receive und Send des Sockets gelöst hat.

    Bis hierhin habe ich den Code nun in meinem Projekt implementiert (Infinity hat sich als netter Email-Kontakt herausgestellt :D ). Das Problem ist nur, dass bei meinem Code der letzte Puffer mit der Fehlermeldung "Offset und Länge für das Array liegen außerhalb des gültigen Bereichs, oder die Anzahl ist größer als die Anzahl der Elemente vom Index bis zum Ende der Quellauflistung." nicht gelesen wird und aufgrund dessen die geschrieben Dateien nicht vollständig sind. Dazu muss ich anmerken, dass dieser Fehler erst bei einer Dateigröße ab ca. 2.1 MB auftritt, weshalb ich vermutete, dass es etwas mit dem Wertebereich einer Integer zu tun haben könnte (beim Debuggen taucht die Zahl -2147024809 bei _HResult auf)...

    Hier mal der Code zum Senden:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. PrivateSub sendSub()
    2. Dim client AsNewTcpClient
    3. Try
    4. client.Connect(TextBox1.Text, TextBox2.Text)
    5. Catch ex AsException
    6. MsgBox("Verbindung mit dem Zielcomputer konnte nicht hergestellt werden.", MsgBoxStyle.Critical + MsgBoxStyle.OkOnly, "Error")
    7. Me.Invoke(Sub() Button1.Enabled = True)
    8. Me.Invoke(Sub() RadioButton2.Enabled = True)
    9. End Try
    10. Dim fstream AsStream = NewFileStream(sendPath, FileMode.Open, FileAccess.Read)
    11. Dim nstream AsNetworkStream = client.GetStream
    12. Dim streamw AsStreamWriter
    13. Dim bytesSent AsLong
    14. Dim buffer(client.SendBufferSize - 1) AsByte
    15. Dim fileSize AsLong = fstream.Length
    16. Try
    17. Me.Invoke(Sub() Label9.Visible = True)
    18. Me.Invoke(Sub() Label10.Visible = True)
    19. streamw = NewStreamWriter(nstream)
    20. streamw.WriteLine(fileSize)
    21. streamw.Flush()
    22. Me.Invoke(Sub() Label12.Text = System.Math.Round((fileSize / 1000000), 2) & " MB")
    23. Me.Invoke(Sub() ProgressBar1.Minimum = 0)
    24. Me.Invoke(Sub() ProgressBar1.Maximum = fileSize)
    25. Me.Invoke(Sub() ProgressBar1.Value = ProgressBar1.Minimum)
    26. While bytesSent + client.SendBufferSize < fstream.Length
    27. fstream.Read(buffer, 0, buffer.Count)
    28. bytesSent += client.Client.Send(buffer, SocketFlags.None)
    29. Me.Invoke(Sub() Label5.Text = System.Math.Round((bytesSent / 1000000), 2))
    30. Me.Invoke(Sub() ProgressBar1.Value = bytesSent)
    31. End While
    32. fstream.Read(buffer, 0, CInt(fstream.Length - bytesSent)) '<----- An dieser Stelle tritt der Fehler auf
    33. client.Client.Send(buffer, CInt(fileSize - bytesSent), SocketFlags.None)
    34. Me.Invoke(Sub() Label5.Text = System.Math.Round((fileSize / 1000000), 2))
    35. Me.Invoke(Sub() ProgressBar1.Value = ProgressBar1.Maximum)
    36. fstream.Close()
    37. nstream.Close()
    38. client.Close()
    39. Catch ex AsException
    40. MsgBox("Datenübertragung fehlgeschlagen.", MsgBoxStyle.Critical + MsgBoxStyle.OkOnly, "Error")
    41. Me.Invoke(Sub() Button1.Enabled = True)
    42. Me.Invoke(Sub() RadioButton2.Enabled = True)
    43. Me.Invoke(Sub() Label9.Visible = False)
    44. Me.Invoke(Sub() Label10.Visible = False)
    45. Me.Invoke(Sub() ProgressBar1.Value = ProgressBar1.Minimum)
    46. End Try
    47. MsgBox("Datenübertragung abgeschlossen.", MsgBoxStyle.Information + MsgBoxStyle.OkOnly, "Information")
    48. Me.Invoke(Sub() Button1.Enabled = True)
    49. Me.Invoke(Sub() RadioButton2.Enabled = True)
    50. Me.Invoke(Sub() Label9.Visible = False)
    51. Me.Invoke(Sub() Label10.Visible = False)
    52. Me.Invoke(Sub() ProgressBar1.Value = ProgressBar1.Minimum)
    53. End Sub


    Und der Code zum Empfangen:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. PrivateSub getSub()
    2. Dim listener AsNewTcpListener(IPAddress.Any, TextBox2.Text)
    3. listener.Start()
    4. Dim fstream AsNewFileStream(getPath, FileMode.Create, FileAccess.Write)
    5. Dim streamr AsStreamReader
    6. Dim client AsTcpClient = listener.AcceptTcpClient
    7. Dim nstream AsNetworkStream = client.GetStream
    8. Dim bytesWritten AsLong
    9. Dim buffer(client.ReceiveBufferSize - 1) AsByte
    10. Dim received AsInteger
    11. Dim fileSize AsLong
    12. Try
    13. streamr = NewStreamReader(nstream)
    14. fileSize = streamr.ReadLine
    15. Me.Invoke(Sub() Label12.Text = System.Math.Round((fileSize / 1000000), 2) & " MB")
    16. Me.Invoke(Sub() ProgressBar1.Minimum = 0)
    17. Me.Invoke(Sub() ProgressBar1.Maximum = fileSize)
    18. Me.Invoke(Sub() ProgressBar1.Value = ProgressBar1.Minimum)
    19. While bytesWritten + client.ReceiveBufferSize < fileSize
    20. received = client.Client.Receive(buffer, SocketFlags.None)
    21. fstream.Write(buffer, 0, received)
    22. bytesWritten += received
    23. Me.Invoke(Sub() Label5.Text = System.Math.Round((bytesWritten / 1000000), 2))
    24. Me.Invoke(Sub() ProgressBar1.Value = bytesWritten)
    25. EndWhile
    26. received = client.Client.Receive(buffer, CInt(fileSize - bytesWritten), SocketFlags.None)
    27. fstream.Write(buffer, 0, received)
    28. Me.Invoke(Sub() Label5.Text = System.Math.Round((fileSize / 1000000), 2))
    29. Me.Invoke(Sub() ProgressBar1.Value = ProgressBar1.Maximum)
    30. fstream.Close()
    31. client.Close()
    32. nstream.Close()
    33. listener.Stop()
    34. Catch ex AsException
    35. MsgBox("Dateiübertragung fehlgeschlagen.", MsgBoxStyle.Critical + MsgBoxStyle.OkOnly, "Error")
    36. Me.Invoke(Sub() Button2.Enabled = True)
    37. Me.Invoke(Sub() RadioButton1.Enabled = True)
    38. Me.Invoke(Sub() Label7.Visible = False)
    39. Me.Invoke(Sub() Label9.Visible = False)
    40. Me.Invoke(Sub() Label8.Visible = False)
    41. Me.Invoke(Sub() ProgressBar1.Value = ProgressBar1.Minimum)
    42. EndTry
    43. MsgBox("Datenübertragung abgeschlossen.", MsgBoxStyle.Information + MsgBoxStyle.OkOnly, "Information")
    44. Me.Invoke(Sub() Button2.Enabled = True)
    45. Me.Invoke(Sub() RadioButton1.Enabled = True)
    46. Me.Invoke(Sub() Label7.Visible = False)
    47. Me.Invoke(Sub() Label9.Visible = False)
    48. Me.Invoke(Sub() Label8.Visible = False)
    49. Me.Invoke(Sub() ProgressBar1.Value = ProgressBar1.Minimum)
    50. EndSub


    Ich hoffe, dass jemand mal über die Codes drüber sehen und mir helfen kann :)

    Gruß

    Wieso die vielen Invokes?

    Versuch die Datei mal in kleine Stücke zu teilen, und diese Stücke dann zu senden. Bin mir ncih sicher, aber ich meine TCP hatn bestimmtes Limit.

    Versuch das doch ersma in ner Konsolenanwendung und lass die schöne Progressbar und die vielen Labels weg.
    Welcher Fehler tritt genau auf? (ArgumentOutOfRangeException, InvalidCasteException?)
    Welche Werte haben die Variablen zum Zeitpunkt des Fehlers?

    Ich kann mir (noch) nicht erklären, wieso es bei dir nicht geht.

    Außerdem muss ich wie Rinecamo sagen, dass es nicht sinnvoll ist, die Invoke-Funktion so oft aufzurufen.
    Normalerweise kapselt man das Senden/Empfangen der Daten in eine eigene Klasse und diese enthält dann ein Event, z. B. StatusChanged welches ausgelöst wird. So habe ich es zumindest auch in meinem Beispiel gemacht.
    Wenn man dann diese Klasse verwendet, kann man VB.Net mit AddHandler sagen, dass beim StatusChanged-Event eine bestimmte Funktion aufgerufen werden soll, und diese macht dann die gewünschten Einstellungen an den Controls (z. B. die ProgressBar.Vale ändern) und führt vorher falls nötig noch einen Invoke aus.

    Schaut dann in meinem Programm z. B. so aus:

    VB.NET-Quellcode

    1. Private Delegate Sub ConnectionStatusChangedDelegate(ByRef Connection As Connection)
    2. Private Sub ConnectionStatusChanged(ByRef Sender As Connection)
    3. If InvokeRequired Then
    4. Invoke(New ConnectionStatusChangedDelegate(AddressOf ConnectionStatusChanged), New Object() {Sender})
    5. Else
    6. 'was im GUI-Thread zu machen ist, z. B. eine RICHTIGE umrechnung von Byte in MB
    7. Label12.Text = String.Format("{0:0.00} MB", fileSize / 1048576)
    8. End If
    9. End Sub

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Infinity“ ()

    Spoiler anzeigen
    Danke für die Antworten.
    Invoke habe ich benutzt, um threadübregreifend Änderungen an der GUI machen zu können.

    Zur Fehlermeldung kann ich Folgendes noch sagen:
    Typ: System.ArgumentException
    Zusätzliche Informationen: Offset und Länge für das Array liegen außerhalb des gültigen Bereichs, oder die Anzahl ist größer als die Anzahl der Elemente vom Index bis zum Ende der Quellauflistung.

    Zu den Variablen:
    (Name : Wert)

    buffer : {Length=65536}
    bytesSent : 67436544
    fileSize : 67520226

    Innerhalb von fileStream hat "GENERIC_READ" den Wert "-2147483648", "Position" entspricht "bytesSent" und "Length" "fileSize".
    Ich hoffe das war alles, was relevant ist und bringt jemanden ein Stück weiter...

    Gruß


    //Edit:
    Habe das Problem eben gelöst. Solange nach Ablauf der Schleife die Differenz aus fileSize und bytesSent größer als 65536 ist, soll der Schleifenrumpf nochmal ausgeführt werden, eben solange, bis das nicht mehr der Fall ist, erst dann soll der letzte Puffer beschrieben werden. Jetzt sind auch die Prüfsummen von Ausgangsdatei und der empfangenen Datei identisch :)

    Vielen Dank an Infinity und Rinecamo, eure Tipps werde ich natürlich dennoch berücksichtigen :)

    Gruß

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