Wie wird ein Buffer bei Socket-Bild-Transfer richtig verwendet ?



  • wenn bedarf besteht kann ich gerne nochmal den kompletten code posten,hab den nämlich grade griffbereit.

    J0



  • hier mein Versuch das umzusetzen, nur jetzt erhalte ich ein überdimensionales Fenster ohne Fehlermeldung 😞
    @J0
    ja, bitte bitte bitte, würde mir wirklich weiterhelfen 🙂

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    TMemoryStream *mem=new TMemoryStream();
    
    Image1->Picture->LoadFromFile(".\\testbild.jpg");
    Image1->Picture->Graphic->SaveToStream(mem);
    mem->Position=0;
    ClientSocket1->Socket->SendText(mem->Size);
    ClientSocket1->Socket->SendBuf(mem->Memory,mem->Size);
    delete mem;
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,
          TCustomWinSocket *Socket)
    {
    TJPEGImage *jpg = new TJPEGImage();
    TMemoryStream *mem=new TMemoryStream();
    int bufsize = StrToInt(Socket->ReceiveText());
    BYTE *Buffer = new BYTE[bufsize];
    
    Socket->ReceiveBuf(Buffer, bufsize);
    mem->Write(Buffer, bufsize);
    mem->Position=0;
    
    jpg->LoadFromStream(mem);
    Image2->Picture->Assign(jpg);
    
    delete [] Buffer;
    delete jpg;
    delete mem;
    }
    


  • Also, da diese frage schon öfters hier gestellt wurde, dachte ich mir schreib ich doch mal ´n kleines Tut (was ich allerdings ohne junix und Jansens Hilfe nicht hinbekommen würde) wie man Streams (hier einen Screenshot) mit dem TClientSocket und dem TServersocket versenden/empfangen kann.

    Benötigte Objekte: (Achtung, an´s Löschen denken!)

    TMemoryStream * Stream = new TMemoryStream;
    

    Erstmal die Größe des Buffers angeben:

    int Buffer = 1024;
    

    Dann natürlich ein JPEGImage um anschliend das Bild in das JPEGImage zu laden:

    TJPEGImage *Screenshot = new TJPEGImage();
    

    Jetzt muss noch der Buffer erstellt werden:

    BYTE *Buffer_BYTEp = new BYTE[Buffer];
    

    Damit, falls vorher schon Daten gesendet wurden die alten Daten nicht überschrieben werden, muss man die Schreib-Position des Streams auf´s Ende des Streams setzen:

    Stream->Position = Stream->Size;
    

    Jetzt empfängt man den Stream mittels ReceiveBuf in den Buffer...

    Socket->ReceiveBuf(Buffer_BYTEp, Buffer);
    

    ...Und schreibt ihn in den Stream.

    Stream->Write(Buffer_BYTEp, Buffer);
    

    Ich setze den Stream vor dem Laden immer auf Position 0, was jedoch optional ist:

    //  Stream->Position = 0;
    

    Naja, jetzt nurnoch den Screenshot in das JPEGImage laden...

    Screenshot->LoadFromStream(Stream);
    

    ...und in eine Datei speichern.

    Screenshot->SaveToFile("Screenshot.jpg");
    

    Jetzt noch aufräumen und fertig ist der Empfang.

    delete [] Buffer_BYTEp;
      delete Screenshot;
    

    Hier nochmal der Code ohne den Text (Code kommt natürlich in die OnRead methode des Clienten):

    {  
      int Buffer = 1024;
      TJPEGImage *Screenshot = new TJPEGImage();
      BYTE *Buffer_BYTEp = new BYTE[Buffer];
      Stream->Position = Stream->Size;
      Socket->ReceiveBuf(Buffer_BYTEp, Buffer);
      Stream->Write(Buffer_BYTEp, Buffer);
      //Stream->Position = 0;
      Screenshot->LoadFromStream(Stream);
      Screenshot->SaveToFile("Screenshot.jpg");
      delete [] Buffer_BYTEp;
      delete Screenshot;
    }
    

    Und wenn wir grade dabei sind: Um Dateien auf diesem wege zu verschicken lässt man einfach das TJPEGImage weg und speichert den Stream.

    Und weil´s so schön ist, hier auch gleich mal das verschicken, was viel einfacher ist *g*:

    Benötigte Objekte (löschen wieder nich vergessen!):

    TJPEGImage *jpg = new TJPEGImage();
    

    Bild Laden:

    jpg->LoadFromFile("Image.jpg");
    

    Jetzt Qualität optional schlechter machen, damit das Bild nicht so groß ist:

    //jpg->CompressionQuality = 20;
      //jpg->Compress();
    

    Stream ersetllen:

    TMemoryStream*Stream = new TMemoryStream;
    //  Stream->SetSize(0);
    //  Stream->Clear();
    

    Bild in den Stream speichern:

    jpg->SaveToStream(Stream);
    

    Ich setze den Stream vor dem Sepichern immer auf Position 0, was jedoch optional ist:

    //  Stream->Position =0;
    

    Jetzt nurnoch senden und fertig is das ganze.

    Form1->ServerSocket1->Socket->Connections[0]->SendStream(Stream);
    

    Man kann auch einfach das Bild direkt in den Stream laden, was aber den Nachteil hat das man das Bild nicht mehr 'verkleinern' kann.

    Dieser code steht ausserdem in Verbindung mit einen Screenshotcode, der den Screenshot in das JPEGImage kopiert.

    Hier nochmal der Code mit den optionalen Sachen...

    jpg->LoadFromFile("Image.jpg");
      jpg->CompressionQuality = 20;
      jpg->Compress();
    
      TMemoryStream*Stream = new TMemoryStream;
      Stream->SetSize(0);
      Stream->Clear();
      jpg->SaveToStream(Stream);
      Stream->Position =0;
      Form1->ServerSocket1->Socket->Connections[0]->SendStream(Stream);
    

    ...und nochmal ohne:

    jpg->LoadFromFile("Image.jpg");
      TMemoryStream*Stream = new TMemoryStream;
      jpg->SaveToStream(Stream);
      Form1->ServerSocket1->Socket->Connections[0]->SendStream(Stream);
    

    Bei irgendwelchen Hinweisen, Fahlern o.ä. bitte Bescheid sagen.

    Schöne grüße,
    J0



  • ersteinmal 1000Dank für deine super Arbeit...
    --> ich habe es nun versucht so umzusetzen, wie du es erklärt hast, nur will es immer noch nicht.
    Als Fehlermeldung erhalte ich "JPEG-Fehler #41" 😕
    Hoffentlich wisst Ihr womit das zusammenhängt.
    --> http://homepages.borland.com/efg2lab/Library/Delphi/Graphics/JpegErrors.txt sagt dazu:
    "41 cstrJERR_INPUT_EMPTY 'Empty input file'"

    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    #pragma hdrstop
    
    #include "Unit1.h"
    #include "Jpeg.hpp"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
      TMemoryStream *mem=new TMemoryStream();
      Image1->Picture->LoadFromFile(".\\marikobild.jpg");
      Image1->Picture->Graphic->SaveToStream(mem);
      mem->Position=0;
      ClientSocket1->Socket->SendBuf(mem->Memory,mem->Size);
      delete mem;
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,
          TCustomWinSocket *Socket)
    {
      TJPEGImage *jpg = new TJPEGImage();
      TMemoryStream *mem=new TMemoryStream();
      int bufsize = Socket->ReceiveLength();
      BYTE *Buffer = new BYTE[bufsize];
    
      mem->Position=mem->Size;
      Socket->ReceiveBuf(Buffer, bufsize);
      mem->Write(Buffer, bufsize);
      //mem->Position=0;
      jpg->LoadFromStream(mem);
      Image2->Picture->Assign(jpg);
    
      delete [] Buffer;
      delete jpg;
      delete mem;
    }
    //---------------------------------------------------------------------------
    


  • ich glaube das Problem ist, das ich per "delete mem" im OnRead Event immer wieder meinen Stream lösche...
    --> wie kann ich das verhindern ?
    Den Stream global deklarieren ?
    oder einfach nicht löschen ???

    ich hoffe auf eure Hilfe 🙂



  • Deklaiere den mal global und lösche den erst später weg - ich lösche im FormClose event und deklaiere global.

    J0



  • mmh, so funktioniert's...
    - aber wie kann ich das mit nur einem Button-Click realisieren ?
    - geht das auch irgendwie ohne globale Variablen ?

    nochmals danke für die Hilfe 👍

    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    #pragma hdrstop
    
    #include "Unit1.h"
    #include "Jpeg.hpp"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    TMemoryStream *mem=new TMemoryStream();
    
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
      TJPEGImage *jpg= new TJPEGImage();
      TMemoryStream *mem=new TMemoryStream();
    
      jpg->LoadFromFile(".\\marikobild.jpg");
      mem->SetSize(0);
      mem->Clear();
      jpg->SaveToStream(mem);
      mem->Position = 0;
      ClientSocket1->Socket->SendStream(mem);
    
      delete jpg,mem;
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,
          TCustomWinSocket *Socket)
    {
      //ShowMessage(mem->Size);
      int bufsize = Socket->ReceiveLength();
      BYTE *Buffer = new BYTE[bufsize];
    
      mem->Position=mem->Size;
      Socket->ReceiveBuf(Buffer, bufsize);
      mem->Write(Buffer, bufsize);
    
      delete [] Buffer;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
         TJPEGImage *jpg= new TJPEGImage();
    
         mem->Position=0;
         jpg->LoadFromStream(mem);
         Image2->Picture->Assign(jpg);
    
         delete jpg,mem;
    }
    //---------------------------------------------------------------------------
    


  • huch, warum war ich denn plötzlich ausgeloggt 😮



  • Anonymous schrieb:

    - geht das auch irgendwie ohne globale Variablen ?

    Da du den Stream hier nur innerhalb des Formulars brauchst, macht es sinn, die Variable im Protected-Bereich des Formulars zu deklarieren und im Konstruktor zu intialisieren und im Destruktor zu zerstören.

    -junix



  • ok, ich habe nun folgendes geändert

    in der Unit1.h

    private:	// Anwender-Deklarationen
            TMemoryStream *mem;
    

    im Konstruktor

    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
    mem=new TMemoryStream();
    }
    

    und dann noch

    void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
    {
    delete mem;
    }
    

    jetzt habe ich noch das Problem mit dem 2ten ButtonClick --> muss ich sicher ein Protokol oder so definieren ? (nur wie geht das) 😞

    bei meinen Versuchen hat sich noch gezeigt, das die Buffergrösse entscheidend ist für die erfolgreiche Übertragung.
    mit

    int bufsize = Socket->ReceiveLength();
    

    funktioniert es zwar, aber das Bild hat Farbfehler...

    bei anderen Buffergrössen erhalte ich wieder den JPEG-Fehler #50.

    --> wie kann ich das optimieren ?



  • ich hab die größe bei mir auf 1024, so bekomme ich nie fehler bei akzeptabler geschwindigkeit. wenn du es komplett fehlerfrei haben möchtest musst du den wert 1 benutzen, was jedoch langsamer ist.

    Musst einfach mal was ausprobieren, ich finde 1024 optimal.

    J0



  • simonch schrieb:

    jetzt habe ich noch das Problem mit dem 2ten ButtonClick --> muss ich sicher ein Protokol oder so definieren ? (nur wie geht das) 😞

    Ich hab den Entpsrechenden Beitrag aus dem velinkten Thread losgekoppelt und in die FAQ geschoben. Schau da mal im Abschnitt "Netzwerk" nach.

    -junix



  • @junix
    du hast zwar sehr gut die Theorie beschrieben, aber wie ich nun meine Dateigrösse vorne an den Stream dranhänge und dann auf der anderen Seite herausfiltere, ist mir noch nicht ganz klar. 😕

    @J0
    bei einer Buffergrösse von 1 ist das Bild zuerst ganz verschwommen und wenn ich das dann nochmal aus dem Stream lese (durch Button2-Click) ohne Fehler...
    bei 1024 erhalte ich JPEG-Fehler #50.
    ...das verschwommene Bild habe ich mit

    int bufsize = 8;
    

    wegbekommen (seltsam) 😮

    Ohje, ich hoffe meine Fragen gehen euch nicht auf die Nerven, aber ohne euch wüsste ich einfach nicht weiter 😞



  • simonch schrieb:

    @junix
    du hast zwar sehr gut die Theorie beschrieben, aber wie ich nun meine Dateigrösse vorne an den Stream dranhänge und dann auf der anderen Seite herausfiltere, ist mir noch nicht ganz klar. 😕

    Ich hasse es zwar mich selber zu zitieren... aber (von der 1. Seite)

    junix schrieb:

    In dem du, bevor du die Daten aus dem Stream verschickst, zunächst die Länge des BIldes verschickst.

    Beim Empfangen das Selbe in umgekehrter Reihenfolge: Bevor du die Daten in den Stream einliest, liest du schön byte für byte in eine andere Variable.[...]

    Vielleicht noch ergänzend: Man kann mit den SendBuf und ReceiveBuf-Funktionen nicht nur Streams versenden (o;

    Wenn du mir präziser sagen würdest, was du nicht verstehst, dann könnte ich dir auch genauer helfen (o;

    -junix



  • simonch schrieb:

    wenn ich das dann nochmal aus dem Stream lese (durch Button2-Click) ohne Fehler...

    kannst du mal den button2-code zeigen? und den dazu passenden code der vorher ausgeführt wird? (du meintest ja, dass du nochmal aus dem stream liest - mit dem selben code?)

    J0



  • der Button2-Code..

    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
         TJPEGImage *jpg= new TJPEGImage();
         mem->Position=0;
         jpg->LoadFromStream(mem);
         Image2->Picture->Assign(jpg);
         delete jpg,mem;
    }
    

    davor wird mehrfach das OnRead-Event ausgeführt

    void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,
          TCustomWinSocket *Socket)
    {
      //ShowMessage(mem->Size);
      int bufsize = 8;//Socket->ReceiveLength();
      BYTE *Buffer = new BYTE[bufsize];
    
      mem->Position=mem->Size;
      Socket->ReceiveBuf(Buffer, bufsize);
      mem->Write(Buffer, bufsize);
    
      delete [] Buffer;
    }
    


  • @junix
    "Wenn du mir präziser sagen würdest, was du nicht verstehst, dann könnte ich dir auch genauer helfen "
    --> ich weiss nicht, wie ich die Grösse des Bildes an meinen Stream dranhänge,d.h.
    mit

    ClientSocket1->Socket->SendBuf(memsend->Memory,memsend->Size);
    

    übertrage ich ja den Stream, nur

    ClientSocket1->Socket->SendBuf(memsend->Size+":"+memsend->Memory,memsend->Size+memsend->Size.Length+1);
    

    ist wohl unmöglich 😃

    weiterhin weiss ich nicht, wie ich meine Bildgrösse am Server rausfiltere ?
    --> damit ich byteweise die Zahlen ermitteln kann, muss ich meinen Buffer wohl auf 1 setzen, was dann wiederum Problem mit den Bilddaten im Stream bringt.
    Ist es vielleicht möglich dann erst bis zum : zu lesen, und dann nachträglich die bufsize zu verändern (bei mir halt halt auf 😎
    Aber wie kann ich auf die einzelnen Zeichen zugreifen und auf ein : überprüfen ?

    Fragen über Fragen 😮



  • Hier mal ein Simpelst-"Protokoll", ausgehend von deinem Beispielcode:

    //--------------------------------------------------------------------------- 
    
    #include <vcl.h>
    #pragma hdrstop 
    
    #include "Unit1.h"
    #include "Jpeg.hpp" 
    //---------------------------------------------------------------------------
    #pragma package(smart_init) 
    #pragma resource "*.dfm"
    TForm1 *Form1; 
    TMemoryStream *mem=new TMemoryStream();
    
    int img_size = 0; // speichert die Grösse der zu übertragenden Daten
    
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner) 
            : TForm(Owner)
    { 
    }
    //--------------------------------------------------------------------------- 
    
    void __fastcall TForm1::Button1Click(TObject *Sender) 
    {
      TJPEGImage *jpg= new TJPEGImage();
      TMemoryStream *mem=new TMemoryStream();
    
      jpg->LoadFromFile("c:\\terminal1.jpg");
      jpg->SaveToStream(mem);
    
      // Grösse der zu übertragenden Daten ermitteln
      img_size = mem->Size;
    
      // Inhalt der ersten 4 Bytes des Streams am Ende anfügen (Integer -> 4 Byte, besser wäre sizeof(int) ;))
      char ch[5];
      mem->Seek(0, 0);
      mem->Read(ch, 4);
      mem->Seek(0, 2);
      mem->Write(ch, 4);
    
      // img_size in den ersten 4 Bytes des Streams speichern, der "Header"
      mem->Seek(0, 0);
      mem->Write(&img_size, 4);
    
      img_size = 0; // zurücksetzen
    
      mem->Seek(0, 0); 
      ClientSocket1->Socket->SendStream(mem);
    
      delete jpg;
      delete mem;
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,
          TCustomWinSocket *Socket)
    {
      int bufsize = Socket->ReceiveLength();
      BYTE *Buffer = new BYTE[bufsize];
    
      mem->Seek(0, 2);
      Socket->ReceiveBuf(Buffer, bufsize);
      mem->Write(Buffer, bufsize);
    
      delete [] Buffer;
    
      if (img_size == 0) // erstes Paket ...
      {
        mem->Seek(0, 0);
        mem->Read(&img_size, 4); // .. also Gesamtgrösse auslesen
      }
      else if (mem->Size == (img_size + 4)) // Stream hat Gesamtgrösse + Headergrösse,
        Button2->Click();                   // dh. die Übertragung ist beendet
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
         TJPEGImage *jpg= new TJPEGImage();
    
         char ch[5];
         // letzten 4 Bytes wieder an den Anfang kopieren, sind ja Teil des Bildes
         mem->Seek(-4, 2);
         mem->Read(ch, 4);
         mem->Seek(0, 0);
         mem->Write(ch, 4);
    
         // die zusätzlichen Bytes abschneiden
         mem->Size -= 4;
    
         mem->Seek(0, 0);
         jpg->LoadFromStream(mem);
         Image2->Picture->Assign(jpg);
    
         delete jpg;
         delete mem;
    }
    //---------------------------------------------------------------------------
    

    Leider gibt's bei den Streams ja kein Insert oder Delete, deshalb muss man ein bischen hin- und herkopieren.



  • hallo Jansen,

    vielen Dank für dein Beispiel 🙂

    ...du wirst es nicht glauben, aber bei mir passiert garnix, wenn ich auf's Knöpfchen (Button1) drücke ???

    hier der nach deinen Vorgaben umgebaute Code, vielleicht weisst du ja woran das liegt ?

    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    #pragma hdrstop
    
    #include "Unit1.h" 
    #include "Jpeg.hpp"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    TMemoryStream *mem=new TMemoryStream();
    int img_size=0;
    
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
      TJPEGImage *jpgsend= new TJPEGImage();
      TMemoryStream *memsend=new TMemoryStream();
    
      jpgsend->LoadFromFile(".\\marikobild.jpg");
      jpgsend->SaveToStream(memsend);
    
      img_size = memsend->Size;
    
      char ch[5];
      memsend->Seek(0, soFromBeginning);
      memsend->Read(ch, 4);
      memsend->Seek(0, soFromEnd);
      memsend->Write(ch, 4);
    
      memsend->Seek(0, soFromBeginning);
      memsend->Write(&img_size, 4);
    
      img_size = 0;
    
      memsend->Seek(0, soFromBeginning);
      ClientSocket1->Socket->SendBuf(memsend->Memory,memsend->Size);
    
      delete jpgsend;
      delete memsend;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,
          TCustomWinSocket *Socket)
    {
      int bufsize = Socket->ReceiveLength();
      BYTE *Buffer = new BYTE[bufsize];
    
      mem->Seek(0, soFromEnd);
      Socket->ReceiveBuf(Buffer, bufsize);
      mem->Write(Buffer, bufsize);
      delete [] Buffer;
    
      if (img_size == 0)
      {
        mem->Seek(0, soFromBeginning);
        mem->Read(&img_size, 4);
      }
      else if (mem->Size == (img_size + 4))
      {
         TJPEGImage *jpg= new TJPEGImage();
    
         char ch[5];
         mem->Seek(-4, soFromEnd);
         mem->Read(ch, 4);
         mem->Seek(0, soFromBeginning);
         mem->Write(ch, 4);
    
         mem->Size -= 4;
    
         mem->Seek(0, soFromBeginning);
         jpg->LoadFromStream(mem);
         Image1->Picture->Assign(jpg);
    
         delete jpg;
         delete mem;
      }
    }
    //---------------------------------------------------------------------------
    


  • Tja, hier funktioniert dein Code einwandfrei. 🙂
    Bist du schon mal durchgesteppt? Wird zB. am Anfang das Bild überhaupt geladen, wird die img_size richtig geschrieben und ausgelesen oder wird die "else if"-Abfrage jemals wahr?


Anmelden zum Antworten