BinaryReader-/Writer & TCP

  • VB.NET

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von n1nja.

    BinaryReader-/Writer & TCP

    Hallo VB-ler,

    da ich nun schon recht lange durch Google und auch dieses Forum stöber um herauszufinden wie nun das senden von Dateien über einen BinaryReader-/Writer und einen NetworkStream funktioniert, hab ich mich jetzt entschlossen mal nachzufragen.

    Folgendes:
    Wie man die Datei schreibt ist kein Problem

    TCP Aufbau ist ebenfalls kein Problem, es hapert nur an der "Fusion" zwischen dem BinaryWriter/-Reader und dem NetworkStream sowie dem BinaryWriter.

    Hat jemand Zeit und Lust es mir zu erklären oder mir einen Link geben wo man es detaillierter nachlesen kann?

    Grüße
    Niklas
    Hi
    wo liegen denn die Probleme da? Bei Tcp handelt es sich halt um einen Stream, der nur Input und Output kann. Wenn keine Bytes vorhanden sind, blockiert der Stream, BIS Daten vorhanden sind, da der Rückgabewert von 0 bei Stream.Read als Stream-Ende interpretiert wird. Ansonsten wird eine Anzahl n mit n <= count Bytes ausgelesen. Da der BinaryReader über Stream.Read arbeitet, blockiert der ebenfalls, da er ja eine festgelegte Anzahl an Bytes auslesen muss. Der BinaryWriter schreibt halt über Stream.Write.
    Der einzige große Unterschied zwischen den beiden Streams ist eigentlich, dass die Eingabe und Ausgabe beim FileStream den gleichen Inhalt bereitstellt, während der Tcp-Stream auf Read empfängt und auf Write sendet.

    Gruß
    ~blaze~
    Ich habe das Problem, dass ich nicht weiß wie ich das ganze angehen soll.
    Ich habe z.B. einen FileStream, den deklarier ich ganz normal im Programm, baue meine TCP Verbindung auf und muss den FileStream dann in den NetworkStream leiten ? Oder wie funktioniert das?
    Kannst du mir das anhand eines Codebeispiels zeigen? Gerne auch Pseudo Code :)

    Grüße
    Quasi so:

    VB.NET-Quellcode

    1. Public Shared ReadOnly StringMessageKind As Guid = New Guid("3bdfdfac-22da-449f-a963-f895586de8e1")

    VB.NET-Quellcode

    1. Dim clientStream As System.IO.Stream = client.GetStream() 'Stream des TcpClients holen
    2. Dim messageData() As Byte = System.Text.Encoding.Unicode.GetBytes("Hello World") 'Nachrichtendaten
    3. Dim messageKind As Guid = StringMessageKind 'ID fuer eine Nachricht (kannste nat. auch weglassen, waer aber praktisch fuer die Zuordnung des Nachrichtentyps)
    4. clientStream.Write(messageKind.ToByteArray(), 0, 16) 'Kind rausschreiben
    5. clientStream.Write(BitConverter.GetBytes(messageData.Length), 0, 4) 'Nachrichtendatenlaenge rausschreiben
    6. clientStream.Write(messageData, 0, messageData.Length) 'Nachrichtendaten rausschreiben

    Beim BinaryWriter funktioniert's dann halt analog. Ich denk' mal, du weißt, wie der BinaryWriter das umsetzt? ;)
    Beim auslesen wandelst du das wieder vom Byte-Stream in die Message um.

    VB.NET-Quellcode

    1. Dim clientStream As System.IO.Stream = client.GetStream() 'Stream des TcpClients holen
    2. Dim br As New System.IO.BinaryReader(clientStream)
    3. Dim messageKind As Guid = new Guid(br.ReadBytes(16))'Id wieder extrahieren
    4. Dim messageData() As Byte = br.ReadBytes(br.ReadInt32()) 'Laenge und sofort Daten auslesen
    5. If messageKind = StringMessageKind Then
    6. MessageBox.Show(System.Text.Encoding.GetString(messageData))
    7. Else
    8. Throw New NotSupportedException()
    9. End If

    oder irgendwie so. ;)

    Gruß
    ~blaze~
    Du brauchst nicht wirklich einen BinaryReader/Writer dazu um Daten hin und her zu schicken.

    Bei Nachrichten bzw Strings unter 1024 Bytes stellt es kein Problem dar.
    Wenn die Dateien größer werden, musst du blockweiße deine Daten hin schicken und wieder blockweiße auslesen.

    Versucht erstma Texte bzw Nachrichten zu senden und empfangen im "Byte-Format".
    Okay! So wie du es geschrieben hast funktioniert es sehr gut :)
    Bedeutet.. das gesamte was du gepostet hast, muss ich nur noch so ummodeln das es das bytearray der datei einliest statt das bytearray eines strings, oder?

    @n1nja:
    Das ist das zweite Problem!
    Ich habe mehrfach gelesen, dass sobald die Strings bzw Daten die max. Länge von 1024 Bytes übersteigen, dass sie gesplittet werden müssen.

    Das senden eines Byte-basierten Strings funktioniert, wie oben gesagt.
    Wie kann ich denn ein Byte-Array, bei einer 10kb großen datei z.B. durch 10 dividieren und diese Pakete dann senden? Weiß der TCPListener automatisch dass die zusammen gehören oder muss ich ihm das mitgeben?

    Grüße
    Na logo musst du ihm das sagen:)

    Beim Dateitransfer hab ich immer Dateiname und Dateigröße übergeben.
    So könnte vllt das senden aussehen:

    VB.NET-Quellcode

    1. Dim str As New IO.FileStream("pfad", FileMode.Open, FileAccess.Read)
    2. str.Flush()
    3. Dim buff As Byte() = Encoding.UTF8.GetBytes("/datei" & CStr(str.Length))
    4. stream.Write(buff, 0, buff.Length)
    5. stream.Flush()
    6. 'hier sendest du erstmal nur das du eine Datei senden willst mit der Größe
    7. Dim leang As Long = str.Length
    8. Dim sum As Long = 0
    9. Dim a As Integer = 0
    10. Dim data(1024) As Byte
    11. Do
    12. a = str.Read(data, 0, data.Length)
    13. stream.Write(data, 0, a)
    14. sum += a
    15. If sum = leang Then
    16. str.Close()
    17. Exit Do
    18. End If
    19. Loop
    20. 'und hier das eigentliche Senden

    Ist nur ungefähr so wie du es machen kannst, aber damit funktioniert es super :)
    Flush vergessen oben nach dem Schreibvorgang. Das gehört schon noch hin ;).

    Schau' dir einfach mal offset und count an bei Stream.Write. Anschließend gibst du vorher an, wie viele Daten noch zu schicken sind und schreibst eine Packetid vorweg. Sobald das Packet komplett übertragen wurde, setzt du es wieder zusammen (z.B. per System.IO.MemoryStream).

    Gruß
    ~blaze~
    Das zusammensetzen geht auch mit nen FileStream.
    Ich würde aber empfehlen sobald der Transfer läuft es als Temporäre Datei zu speichern und wenn er fertig ist, kannst dahin kopieren wo du willst.

    Hier mal mit nen FileStream das lesen:

    VB.NET-Quellcode

    1. Private Sub Speichern(Byval Input as String, Byval nwstream as Networkstream)
    2. Dim leange As Integer = CInt(input)
    3. Dim a As Integer = 0
    4. Dim data(1024) As Byte
    5. Dim sum As Long = 0
    6. Dim str As New FileStream("pfad", FileMode.Create, FileAccess.Write)
    7. Do
    8. a = nwstream.Read(data, 0, data.Length)
    9. mw.Write(data, 0, a)
    10. sum += a
    11. If sum = leange Then
    12. str.Flush()
    13. str.close()
    14. Exit Do
    15. Loop

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

    Okay, das ist recht einfach eigentlich :)

    Ich denke, das Writen ist auch nicht so schwer.. wo ich im dunkeln tappe ist eben das Empfangen der Daten :(
    Die Pakete müssen ja geöffnet werden und dann aneinander gefügt werden, passiert das ebenfalls mit einem ByteArray?

    #EDIT:
    Okay, die letzten 3 Beiträge kamen während ich geschrieben habe.

    Also das zusammenfügen ist wirklich nichts anderes als das splitten, nur andersherum?

    Und das mit der Paket ID meinst du z.B. so:
    /datei/1/<Bytes>
    /datei/2/<Bytes>
    ?
    Wie würde man denn deinen Snippet ausführen?
    Also deinen Snippet versteh ich nicht ganz

    #EDIT:
    Also das einzige, was ich nicht verstehe ist wo die länge des paketes herkommt.

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

    Ja, genau.
    Aber ich bin ratlos wie ich die Länge rausbekomme.
    Habs so getestet:

    VB.NET-Quellcode

    1. Dim messagedata() As Byte = br.ReadBytes(br.ReadInt32())
    2. MessageBox.Show(System.Text.Encoding.Unicode.GetString(messagedata))


    Aber die MessageBox wurde nicht angezeigt, ein Fehler kam ebenfalls nicht.
    Wir reden einandervorbei denke ich :D
    Ich meine diesen Teil:

    VB.NET-Quellcode

    1. Dim leange As Integer = CInt(input)

    Der String "input", der wird wenn man die Funktion aufruft übergeben, ich weiß aber nicht was ich übergeben soll, abgesehen davon das es logischerweise die Länge der Datei sein muss die gesendet werden soll. Aber wie komm ich denn an die ran?
    Eigentlich muss ich doch das erste Paket auseinandernehmen, Splitten und dann die Länge die eingetragen ist als input verwenden oder?
    Ach das, sry. Vorher musst du vom Stream lesen am besten in ne extra Sub. Beim lesen des Streams überprüft du das was raus kommt. Wenn dann zb der String mit /Datei anfängt kannst du auf der Speichern Sub verweißen. Das /Datei noch replacen bzw löschen dann hast die Länge. Du du ja beim senden /Datei + Länge sendest. Hoff war jetzt verständlich:)
    Pseudo weiß ich was ich machen muss D:
    In der Umsetzung harkt es aber gewaltig.

    Senden tut er es so:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Try
    2. Dim open As New OpenFileDialog
    3. If open.ShowDialog = Windows.Forms.DialogResult.OK Then
    4. tcp_client.Connect("127.0.0.1", 8000)
    5. If tcp_client.Connected Then
    6. stream = tcp_client.GetStream
    7. 'Dim messagedata() As Byte = System.Text.Encoding.Unicode.GetBytes("Hello World!")
    8. 'Dim messagekind As Guid = StringMessageKind
    9. 'stream.Write(messagekind.ToByteArray(), 0, 16)
    10. 'stream.Write(BitConverter.GetBytes(messagedata.Length), 0, 4)
    11. 'stream.Write(messagedata, 0, messagedata.Length)
    12. Dim str As New IO.FileStream(open.FileName, FileMode.Open, FileAccess.Read)
    13. str.Flush()
    14. MessageBox.Show(str.Length)
    15. Dim buff As Byte() = System.Text.Encoding.UTF8.GetBytes("data/" & CStr(str.Length))
    16. stream.Write(buff, 0, buff.Length)
    17. stream.Flush()
    18. Dim leang As Long = str.Length
    19. Dim sum As Long = 0
    20. Dim a As Integer = 0
    21. Dim data(1024) As Byte
    22. Do
    23. a = str.Read(data, 0, data.Length)
    24. stream.Write(data, 0, a)
    25. sum += a
    26. If sum = leang Then
    27. str.Close()
    28. Exit Do
    29. End If
    30. Loop
    31. stream.Flush()
    32. End If
    33. End If
    34. Catch ex As Exception
    35. MsgBox(ex.Message)
    36. End Try


    Empfangen so:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Sub server()
    2. Try
    3. tcp_server = New TcpListener(ipendpoint)
    4. tcp_server.Start()
    5. tcp_client = tcp_server.AcceptTcpClient
    6. stream = tcp_client.GetStream
    7. Dim br As New BinaryReader(stream)
    8. 'Dim messagekind As Guid = New Guid(br.ReadBytes(16))
    9. Dim messagedata() As Byte = br.ReadBytes(br.ReadInt32())
    10. 'If messagekind = StringMessageKind Then
    11. ' MessageBox.Show(System.Text.Encoding.Unicode.GetString(messagedata))
    12. 'End If
    13. Dim laenge As Integer = 1334
    14. Dim a As Integer = 0
    15. Dim data(1024) As Byte
    16. Dim sum As Long = 0
    17. Dim str As New FileStream("test.txt", FileMode.Create, FileAccess.Write)
    18. Dim mw As New MemoryStream(messagedata)
    19. Do
    20. a = stream.Read(data, 0, data.Length)
    21. mw.Write(data, 0, a)
    22. sum += a
    23. If sum = laenge Then
    24. str.Flush()
    25. str.Close()
    26. Exit Do
    27. End If
    28. Loop
    29. Catch ex As Exception
    30. End Try
    31. End Sub


    Die laenge habe ich erstmal so übergeben damit der wenigstens irgendwas hat.
    Ich weiß nicht was, aber irgendwas stört mich an dem Code. Teilweise seh ich da keine Logik.
    Was ist da nicht logisch?
    Datei wird eingelesen, Größe zuerst verschickt.
    Stream wird überprüft was ankommt, Dateittansfer, Größe wird ausgelesen und in Schleife wird Datei zusammengesetzt.

    Und mach die Tray-Catch weg.
    Und das auslesen aus den Stream muss in einer Schleife passieren, da es immer wieder passiert...
    Grundgerüst von ner TCP Verbindung sind im Forum genug Tuts. Am besten Schau dir das von Kevin an!