tcp/ip bitmap als byte-array übertragen - vermutlich probleme beim zusammenstückeln der datenpakete

  • VB.NET

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    tcp/ip bitmap als byte-array übertragen - vermutlich probleme beim zusammenstückeln der datenpakete

    da tcp/ip, bzw. sockets für mich noch neugebiet sind, sie aber zweifelsohne zu den grundlagen gehören, versuche ich das ganze mal zu erlernen.....

    das bitmap zu serailzen, sowie deserializen stellt kein problem dar - auch das senden des ganzen byte-arrays dürfte funktinoieren. das empfangen udn zusammenstückeln der einzelnen datenpakete bereitet mir aber probleme.

    das resultierende empfangene byte-array lässt sich nihct wieder in ein bitmap umwandeln - muss also im laufe des sendevorganges beschädigt worden sein.

    da das ganze nach dem zufallsprinzip mal funktioniert und mal nicht, ich den fehler auch nicht wirklich weiter eingrenzen konnte, bitte ich euch mal um rat.

    folgender ansatz:

    sendevorgang

    VB.NET-Quellcode

    1. Dim clientSocket As New System.Net.Sockets.TcpClient()
    2. Dim serverStream As NetworkStream
    3. clientSocket.Connect("127.0.0.1", 930)
    4. serverStream = clientSocket.GetStream()
    5. Dim rect As Rectangle = My.Computer.Screen.Bounds
    6. Dim bmp As Bitmap = New Bitmap(rect.Width, rect.Height)
    7. Dim g As Graphics = Graphics.FromImage(bmp)
    8. g.CopyFromScreen(0, 0, 0, 0, bmp.Size)
    9. Dim buffer() As Byte = Serialize(bmp)
    10. serverStream.Write(buffer, 0, buffer.Length)
    11. serverStream.Flush()



    empfang

    VB.NET-Quellcode

    1. Private ipAdress As IPAddress = Dns.GetHostAddresses("127.0.0.1")(0)
    2. private listener As New TcpListener(ipAdress, 930)
    3. Private client As TcpClient
    4. Private network As NetworkStream
    5. listener.Start()
    6. client = listener.AcceptTcpClient()
    7. network = client.GetStream()
    8. Dim length As Long = 0
    9. Dim store() As Byte
    10. Dim bmp As Bitmap
    11. Do
    12. ReDim Preserve store(length + client.ReceiveBufferSize - 1)
    13. network.Read(store, length, client.ReceiveBufferSize)
    14. length = store.Count()
    15. Loop While network.DataAvailable
    16. bmp = Deserialize(store)



    das andauernde redimmen des arrays ist nur ein schneller schuss aus der hüfte - wird im endeffekt vermutlich durch eine arraylist ersetzt....



    der vollständigkeit halber:
    (de)serialize bitmap

    VB.NET-Quellcode

    1. Private Function Serialize(ByVal data As Image) As Byte()
    2. Dim M As New IO.MemoryStream
    3. data.Save(M, System.Drawing.Imaging.ImageFormat.Bmp)
    4. M.Flush()
    5. Return M.ToArray()
    6. End Function
    7. Private Function Deserialize(ByVal data As Byte()) As Image
    8. Dim m As IO.MemoryStream = New IO.MemoryStream(data)
    9. Dim bm As Image = Image.FromStream(m)
    10. Return bm
    11. End Function

    dann vergleich doch mal die beiden byteArrays und finde heraus, wo die Unterschiede liegen. Ich kenne mich mit der Netzwerkprogrammierung nicht so besonders aus, kann mir aber gut vorstellen, dass einige bytes als steuer-befehle interpretiert werden und/oder verloren gehen. Vllt ist der Byte-Array auch nur ein element zu lang? Einfach mal Haltepunkte setzten und vergleichen - oder ne extra funtion dazu schreiben.
    lg
    und dir ist bewusst, dass du
    a) einen kleinen array verschicken kannst - z.b. alle werte von 0 - 255 - und guckst dann, was angkommt
    b) du liest beim server die exakt selbe bilddatei ein - so hast du den soll-array und den empfangenen array. Da du Programmierer bist wärst du dumm jedes element selbst zu checken, eine einfache schleife macht dir das leben leicht:

    VB.NET-Quellcode

    1. for i = 0 to arr.lenght-1
    2. if sollArr(i) <> istArr(i) then console.writeline("Bug@" & i )
    3. next

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

    lol, du hast da aber garkeine arbeit geleistet (wage ich zu behaupten).
    Erster versuch (nachdem ich deine paar zeilen zusammenhanglosen codes zum laufen bekommen habe):

    Gesendet werden: 7056k einträger
    Empfangen werden: 32k einträge
    Man kann also sagen, dass nichts ankommt xD

    Daher vermute ich mal stark, dass deine Server-Clientverbindung nicht taugt. Bei dem Code lasse ich mal keinen anderen Schluss zu^^
    Schon mal über verwendung einer TCP-Lib (aus dem Showroom) nachgedacht? Da hättest du die ganzen Probleme nicht und könntest Event-basierend programmieren, was die Sache enorm erleichtern würde. Später (wenn alles andere funst) eine eigene Server-Client-Verbindung einzuprogrammieren wäre immernoch möglich, wenn du das in deinen weiteren Schritten bedenkst.

    Edit: ArrayList fail, nutze doch eine ListOf
    lg

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

    dieser code hier ist das resultat von einer ganzen reihe von versuchen - und, was vieleicht das komische daran ist: er funktinoietr hin und wieder.

    es ist mir damit einige male gelungen einen screenshot korrekt zu übertragen - obwohl ich mich langsam frage, wie das möglich war :D

    - aber du hast recht, wenn du darauf anspielst, dass ich von socket-programmierung (noch) keine ahnung habe
    Benutze eine fertige Lib und beschäftige dich mit Sockets später, dann aber richtig^^

    Tipp: .BMP ist kein gutes format für Screenshots, .PNG ist 1000x besser und hat afaik auch kein qualitätsverlust. Hab mal Screenshots verglichen:
    bmp = 5MB
    jpg = 180KB
    PNG = 140KB
    wobei PNG und BMP exakt gleich aussehen, JPG taugt nicht^^

    Tipp2: Die meisten fertigen libs erlauben nur das senden/empfangen von strings. Wandel den Byte-Array mittels convert.ToBase64String in ein einen String und mit convert.FromBase64String wieder zurück.
    Das wäre auch ein ansatz um das "funktioniert hin und wieder" in den griff zu bekommen. Ich vermute mal, dass gewisse Farben bestimmte Binärdaten erzeugen die dann falsch interpretiert werden. Ob es stimmt weiß ich nicht aber iworan muss es ja liegen^^
    auf ein anderes bildformat zu wechseln ist schon mal ein guter tipp, danke- werde ich machen!

    das ganze aber einfach als string zu senden, wo ich es doch eh schon als byte-array vorliegen haben, stößt mit aber ein klein wenig auf :D

    da ich das ganze im grunde derzeit in keinem projekt brauche, spar ichir das erwenden einer bestehenden lib - möchte mich einfach mal mit sockets auseinandersetzen ;)

    werde mich wohl noch etwas einlesen müssen ....

    trotzdem gleich mal ein großes Danke für deine Hilfe!
    bitmaps als String zu übertragen halte ich für eine Gräuseligkeit. Es gibt zwar eine sichere Konvertierungsmöglichkeit - Stichwort Base64-String, aber so ein Base64-String belegt den vielfachen Platz der ursprünglichen Byte-Folge - ich glaub Faktor 6.
    Ich empfehle, dich mit TcpClient/TcpListener auseinanderzusetzen, denn TcpClient ist darauf designed, über Streams, sog NetworkStreams zu kommunizieren.
    Du kannst also die Bitmap als Png in einen Stream schreiben, und auf der anneren Seite als Image.FromStream wieder rekonstruieren.

    Ist aber nicht so einfach, wie's jetzt klingt, vorher ist noch umfassend zu konzipieren: Ein NetworkStream ist ein unendlicher Stream, könnte sein, dass Image.FromStream damit nicht klarkommt. Also musst du zuerst die Anzahl der Bytes übermitteln, dann die Bytes selbst. Der Empfänger liest dann zunächst die Anzahl, dann liest er die Anzahl an Bytes in einen MemoryStream und daraus kannst du die Bitmap basteln.
    Evtl. mußt du ein richtiges Protokoll implementieren, falls noch annere Sachen als nur Images transportiert werden sollen.
    Etwa Befehle, wie "gib mir nächstes Bild!" oder sowas - muß ja an den Server gesendet werden. Und vlt. auch "Hier kommt neues Bild!" - was evtl. an den Client zu senden wäre. Oder "Client bereit zum Empfang von Bild" (an den Server zu senden).
    Und dergleichen.

    Um diese Konzipierung eines Protokoll kommst du imo nicht herum - auch nicht mit einer String-basierten Tcp-Lib.
    keine sorge - ein byte-array erst in ein string umwandeln, um dann wieder zu byte-array und schließlich zu bild zu gehen hätte ich nicht gemacht ;)

    wie du oben siehst, arbeite ich bereits mit memory-streams - das funktioniert ansich ausgezeichnet, problem ist "lediglich" die korrekte übertragung des byte-arrays.

    vermutlich ist das problem aber in der tat bereits gelöst, wenn ich vor jeder bildübertragung die anzahl der zu übertragenden bytes sende.

    ich möchte jedoch nur bei der ersten übertragung das gesamte bild, bzw. array schicken, bei allen folgenden nur mehr die änderungen - geplant ist ein desktop stream über tcp ip.

    protokolmäsig schwebt mir folgendes vor: der erste übertragene byte gibt die art der übertragung wieder (ganzes bild, oder nur teil eines bildes), der zweite byte gibt mir die anzahl der nachfolgneden bytes an, die nötig sind um die länge des gesamtstreams zu kodieren, das ganze eben gefolgt von der gesamten streamlänge.

    eine frage stellt sich mir hier allerdings: wie schaut das ganze dann firewallmäsig aus - kann ich mit komplikationen rechnen?
    @ErfinderDesRades
    ich glaub Faktor 6


    Man sieht, dass aus den Ursprünglichen 3 Bytes im Base64 Format 4 Bytes werden. Laut Wiki steigt das Volumen um 33–36% (=> Faktor 0.35) - ist imo also noch zu verkraften. Mit String meinte ich auch einen Base64-String^^

    @Prometheus
    der erste übertragene byte

    Ein Byte entspricht einer Zahl von 0-255. Reicht damit nicht aus um die Länge des Arrays zu übertragen. Ein Integer wäre das Richtige - glaube 4 Byte ist er lang (der Standard-Integer).
    Funktionieren dürfte das auch, musst die ersten paar Bytes dann mittels bitshift zu einem Integer shiften (oder gibt es da was einfacheres?) bzw den Integer zu vier Bytes, damit du ihn in deinem Byte-Array übertragen kannst.

    Das Thema Screenshot übertragen war schon öfter hier, die Kernaussage von den meisten war, dass man das Bild Stückweise übertragen soll, immer nur die Teile, die sich verändert haben. Ansonsten haut das von der Geschwindigkeit nicht hin. Schau dir mal an wie das PNG-Format schwankt, ein Screenshot kann von 100KB bis zu 3MB brauchen. Kannst dir ja ausrechnen wieviele komplett-Bilder du dann pro Sekunde übertragen kannst...

    lg
    @FreakJNS:

    ich meinte das etwas anders ;) - ich wandle die länge des arrays in eine zahl zur basis 255 um, wobei diese zahl wiederum ein byte-array darstellt - jedes element entspricht einer "ziffer" (in einem system zur basis 255, also einer zahl von 0 bis 255)

    der erste übermittelte byte übergibt mir die anzahl an elementen des zuvor generierten arrays - also die anzahl an bytes, die gelesen werden müssen, um die gesamte zahl zur basis 255 eingelesen zu haben.

    diese dann wieder ins dezimale umgewandelt gibt mir die anzahl an einzulesenden byte wieder

    FreakJNS schrieb:

    Man sieht, dass aus den Ursprünglichen 3 Bytes im Base64 Format 4 Bytes werden.

    naja - ich war auch unsicher. Auch habich gedacht, dass ein Char ja nicht 8 Bit belegt, sondern 16 Bit.
    Aber auch der Gedanke ist vergleichsweise irrelevant, denn das Encoding "komprimiert" den 16-bit-Unicode-Char, wie er im Speicher vorliegt, dann ja wieder auf 8bit.
    Also den Einwand nehme ich zurück: Soo schlimm ist eine Übertragung per Base64-String denn doch nicht.
    :thumbup: