C#: png via Netzwerk erhalten



  • Hallo,
    ich nutze einen Dienst, mit dem ich mir ein szenario zusammenbauen kann und anschließend auf Anfrage als png zugeschickt bekomme.

    Sieht also ungefähr so aus:

    sockel.Send(System.Text.Encoding.ASCII.GetBytes("Anweisung1:x;Anweisung2:y;..."));
    sockel.Send(System.Text.Encoding.ASCII.GetBytes("Befehl für das png");
    while(sockel.Receive(buffer) == buffer.length)
     irgend wie zusammenbauen
    

    Im Byte-Array der Anweisung kann sehr viel stehen. Gibt es dort ein maximum?

    Mein Hauptproblem ist aber eigentlich das Auslesen. Ich weiß, dass mir ein png zugeschickt wird, wie ich damit umgehen muss damit ich dann das png auch habe nicht.
    Kann mir wer helfen?
    Bin auch allgemein für Tipps dankbar. Wie ihr wohl an dem Code-Beispiel (das ist wirklich nur beispielhaft, nur damit ihr wisst wie ich es umsetze) sehen könnt, bin ich bei Netzwerkverbindungen via C# ein ziemlicher Anfänger. Vielleicht gibts ja einen viel besseren Weg.

    Vielen Dank



  • Ich weiß nicht, ob es ein Maximum gibt, aber vermutlich muss das irgendwie der Fall sein. Schließlich gibt es nicht unendlich Speicher. In dem Fall müsstest du deine Anweisungen eben einfach auf mehrere Zeilen verteilen, also auch kein Problem.

    Du musst die PNG-Datei binär öffnen und dann den Inhalt der Datei versenden. Der Client speichert die empfangenen Daten wieder in einer Datei und hat somit die Datei erhalten.

    Damit das möglichst gut funktioniert, kannst du noch den Dateinamen und die Dateigröße mitsenden. Dann weiß der Client wie viel er noch lesen muss (ermöglicht Fortschrittsbalken) und kann die Datei unter dem korrektem Namen abspeichern.



  • Hallo,
    danke für die Antwort.

    Leider ist das ein Dienst den ich zwar nutzen aber nicht abändern kann.
    Leider gibt es auch keine Dokumentation (außer dass der Befehl aufgeführt wird). Ist ein Testprogramm für Vorführungen innerhalb der Firma.

    Soweit ich weiß bekomme ich die Daten die ich dann in eine Datei schreiben muss und als png schreiben - bin mir aber nicht sicher.
    Wie ich das am effizientesten mache ist mir allerdings unklar.


  • Administrator

    Muss gleich auf den Zug, aber ein zwei Dinge kann ich noch schnell hinschreiben, sonst schaue ich später nochmals vorbei:
    1. Schau dir den NetworkStream an. Damit kannst du dann die verschiedenen Stream Möglichkeiten aus System.IO verwenden. Zum Beispiel einen StreamWriter .
    2. Beim einlesen könntest du immer kleine Blöcke (z.B 4096 Bytes) einlesen, so lange Daten reinkommen. Diese Blöcke schreibst du dann hintereinander in ein File. Sofern das PNG wirklich als PNG-File übermittelt wird. Wäre aber noch praktisch zu wissen, wie das PNG genau übertragen wird. Irgendwo muss das doch stehen. Sonst frag halt nach ...

    und ... ehm, ok, muss auf den Zug :p

    Grüssli



  • Hallo,
    Danke für die Antwort.

    Als du mir den NetworkStream vorgeschlagen hast ist mir die Idee gekommen, dass ich doch gar nicht puffern muss. Ich kann ihm doch direkt einen Stream zu einer Datei übergeben. Verbesser mich wenn das nicht geht.
    Das werd ich gleich Morgen in der Arbeit mal testen.

    PS: Was machst du AUF einem Zug?


  • Administrator

    So ... *sich notiert: Nie während der Fasnacht/Karneval den öffentlichen Verkehr benutzen* 🤡

    Also:
    1. Verwende den TcpClient , macht dir das Leben bei TCP einfacher, anstatt alles über eigene Sockets zu machen.
    2. Um die Strings zu senden, verwende die Methode GetStream des Clients. Erstell dann einen StreamWriter und gib gleich die Kodierung an.
    3. Vergiss nicht einen Flush am Ende durchzuführen, bevor du anfängst Daten zu empfangen.
    4. Um die Daten zu erhalten, verwende erneut den Stream , welchen du per** GetStream **erhalten hast.
    5. Unter .Net 4.0 kannst du die Methode Stream.CopyTo verwenden, um den Inhalt des NetworkStream in einen FileStream zu schreiben. Falls du .Net 4.0 nicht verwenden kannst, dann musst du dies halt selber umsetzen, ca. so:

    static class StreamExtension
    {
      public static void CopyTo(this Stream self, Stream dest)
      {
        byte[] buffer = new byte[4096];
        int totalRead = self.Read(buffer, 0, buffer.Length);
    
        while(totalRead > 0)
        {
          dest.Write(buffer, 0, totalRead);
          totalRead = self.Read(buffer, 0, buffer.Length);
        }
      }
    }
    

    Ich bin hier nun davon ausgegangen, dass du wirklich ein PNG-File zugesendet bekommst, dass du TCP verwendest und dass die Verbindung vom Server geschlossen wird, sobald das File übertragen wurde. Falls diese Annahmen nicht zutreffen, dann musst du dies halt mitteilen.

    OT:
    Sagt man auf Hochdeutch nicht: "Auf den Zug gehen"? Wenn man einen Zug erwischen möchte? Zumindest im Schweizerdeutschen sagt man: "Uf e Zug gah". Das 'Uf' bedeutet 'Auf'. 😉

    Grüssli



  • Naja klingt irgendwie als wenn du irgendwo in Indien auf einen der überfüllten Züge willst anstatt in den Zug 😃 Aber wir wissen ja was du meinst Dravere 🙂



  • Danke für eure Hilfe, hat wunderbar geklappt.

    Erst ging nichts. Deshalb habe ich meine eingelesene png-Datei im Klartext mit einer richtigen verglichen. Dabei ist mir aufgefallen, dass vor dem richtigen Header noch was anderes stand.
    So fand ich raus, dass die ersten 4 Bytes mir anzeigen, dass nun ein PNG kommt (eigentlich steht das schon im png Header aber ok) und die näcshten 4 Bytes ist die Länge des PNGs - die brauch ich auch, da der Server die Verbindung NICHT schließt, wenn das Bild zu ende ist (sonst könnte es sein, dass er bis zum Sankt Nimmerleinstag wartet, wenn ich es synchron mache).

    Es funktioniert aber 2 Unschönheiten sind drin. Vielleicht könnt ihr mir helfen.
    Das eine ist das oben beschriebene Auslesen - also dass die Gefahr besteht, dass er iwig wartet, weil vielleicht schon alles da ist. Normal dürfte das nicht passieren, ich habe ja die Länge. Aber beim Computer sollte man niemals nie sagen.

    Im Moment sieht es so aus:

    while (counter < pngSize)
    {
        totalRead = input.Read(buffer, 0, buffer.Length);
        output.Write(buffer, 0, totalRead);
        counter += totalRead;
    }
    

    Kann ich dem einen Timeout mitgeben?

    Das zweite ist, dass die Länge des pngs mit BitConverter.ToInt32 umwandle. Die Bytes müssen aber erst umgedreht werden.
    Da ich nicht nur diese Bytes eingelesen habe, kommt ein Array.Reverse nicht in Frage. Deshalb habe ich es im Moment so gemacht

    BitConverter.ToInt32(new byte[]{buffer[7], buffer[6], buffer[5], buffer[4]}, 0)
    

    Wirklich schöner Code ist das nicht. Weiß wer eine Alternative?


  • Administrator

    Xenya schrieb:

    Kann ich dem einen Timeout mitgeben?

    Einfach die Dokumentaton zu TcpClient lesen:
    http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.receivetimeout.aspx

    Xenya schrieb:

    Weiß wer eine Alternative?

    Mehrere:
    - Wie wäre es mit Bit-Schiebeoperationen?
    - Oder IPAddress.NetworkToHostOrder ? Zum Beispiel kombiniert mit einem BinaryReader .

    Grüssli



  • Hallo,
    ich hätte noch eine Frage:
    Wie kann ich das png noch sinnvoll komprimieren?

    Es muss nichtmal ein png sein, es reicht auch ein jpg.

    Ich habe es also nun jetzt in einen FileStream eingelesen. Wie soll ich am besten vorgehen um es nun so klein wie möglich zu speichern?

    Danke



  • Xenya schrieb:

    Hallo,
    ich hätte noch eine Frage:
    Wie kann ich das png noch sinnvoll komprimieren?

    Es muss nichtmal ein png sein, es reicht auch ein jpg.

    Ich habe es also nun jetzt in einen FileStream eingelesen. Wie soll ich am besten vorgehen um es nun so klein wie möglich zu speichern?

    Danke

    png und jpg sind komprimierende Verfahren, jpg sogar ein sehr effizientes. Da ist nachtröglich nichts mehr zu komprimieren. "So klein wie möglich" ist dabei eine Abwägung zwischen Qualität und Größe. Sicher kannst Du jedes jpg z.B. auf 1kb runterbekommen, nur ob dann noch was zu erkennen ist sei dahingestellt.

    Im Falle von jpg kannst Du normalerweise die Qualität wählen von 0% - 100% (Wobei 0% eher akademisch ist) Das hat dann direkte Auswirkung auf die Dateigröße.



  • loks schrieb:

    Xenya schrieb:

    Hallo,
    ich hätte noch eine Frage:
    Wie kann ich das png noch sinnvoll komprimieren?

    Es muss nichtmal ein png sein, es reicht auch ein jpg.

    Ich habe es also nun jetzt in einen FileStream eingelesen. Wie soll ich am besten vorgehen um es nun so klein wie möglich zu speichern?

    Danke

    png und jpg sind komprimierende Verfahren, jpg sogar ein sehr effizientes. Da ist nachtröglich nichts mehr zu komprimieren. "So klein wie möglich" ist dabei eine Abwägung zwischen Qualität und Größe. Sicher kannst Du jedes jpg z.B. auf 1kb runterbekommen, nur ob dann noch was zu erkennen ist sei dahingestellt.

    Im Falle von jpg kannst Du normalerweise die Qualität wählen von 0% - 100% (Wobei 0% eher akademisch ist) Das hat dann direkte Auswirkung auf die Dateigröße.

    hallo,
    Danke für die Antwort.
    Da das Bild nur eine generierte Karte ist, ist sie also eh nicht sehr detailreich. Das heißt, wenn die Qualität runter gedreht wird, dürfte nicht viel verloren gehen. Da würde ich mich einfach mal damit spielen, wie weit ich es verkleinern will.

    Wie mache ich das am sinnvollsten, dass ich aus meiner png Datei, den ich in einem Datenstream habe, ein möglichst kleines jpg bekomme?
    Mit dem Datenstream speichern und anschließend als ImgStream laden und als jpg speichern? Wie kann ich die Qualität (also deine genannten 0-100%) angeben?

    Danke


  • Administrator

    Statt dass du den erhaltenen Stream in einen FileStream kopierst, erstelle daraus ein Bitmap Objekt:
    http://msdn.microsoft.com/en-us/library/z7ha67kw.aspx

    Danach kannst du die folgende Save -Methode verwenden:
    http://msdn.microsoft.com/en-us/library/ytz20d80.aspx

    Auf dieser Seite findest du auch gleich ein Beispiel, wie man die Qualität von einem Jpeg anpasst. Auf der nachfolgenden Seite gibt es ebenfalls ein Beispiel dazu, wobei das Jpeg Format ein wenig anders ausgewählt wird:
    http://msdn.microsoft.com/en-us/library/system.drawing.imaging.encoderparameter.aspx

    Ich würde vorschlagen, dass du den Jpeg-Encoder irgendwo beim Aufstarten hervorsuchst, damit du nachher diese Suche nicht immer erneut wiederholen musst.

    Grüssli



  • Hallo,
    danke für die Antwort. Leider komme ich erst heute dazu, mich wieder damit zu beschäftigen.

    Ich habe das Problem, dass ich dem Bitmap-Objekt nicht direkt den netzwerk-Stream geben kann, von dem das png kommt. Der Netzwerk-Stream wird nicht geschlossen vom Server. Das heißt, das Bitmap-Objekt kann nicht von dem Stream lesen.
    Ich muss das Bild also in einem Stream zwischen speichern. Was nehme ich für diesen Zweck am besten für einen Stream her? Erst in einen Datenstream zu schreiben und dann an Bitmap übergeben, wird wohl auch nicht Sinn und Zweck sein.

    Oder gibt es gar eine geschicktere Methode, das Bild dem bitmap-Objekt korregt zu übergeben?

    Danke



  • wie wärs mit MemoryStream ?
    Aus dem kannst du dann ein Bitmap/Image erstellen 🙂


Anmelden zum Antworten