Jump to content
Lindawb

WSocket1 send image as memory stream

Recommended Posts

Hello,

I just started using ICS, is there any way to use WSocket and send image as memory stream , if yes I really appreciate any help I get, I found few answers but I have no idea how to buffer and chunk or like that not a professional developer in this matter. I tried this

 

var
 fstream: TMemoryStream;
 MyFormJpeg,  MyJpegImage: TJPEGImage;

begin
  

    MyFormJpeg := TJPEGImage.Create;
    MyFormJpeg.CompressionQuality := 75;
    MyFormJpeg.LoadFromFile('C:\temp\mycat.jpg');
 


        fstream := TMemoryStream.Create;
        try

          MyFormJpeg.SaveToStream(fstream);

          fstream.Position:= 0;

         //WSocket1.BufSize:=fstream.Size;
         WSocket1.Send(fstream, fstream.Size);  //  wsocket1.Send(MemStream.Memory, MemStream.Size); not working
         //WSocket1.Send(PChar(fstream), fstream.Size * SizeOf(Char));

       
        finally
         fstream.Free;
          MyJpegImage.Free;
        end;

also I can't find way to read on the server side, if possible to help

 

Thank you
 

 

Share this post


Link to post
11 hours ago, Lindawb said:

wsocket1.Send(MemStream.Memory, MemStream.Size);

This is the way to send the content of the stream.

But you have a problem for the receiver: how will the receiver know the length of the stream? It would be OK if this is the only thing sent for the whole session. Not a very good design. You should probably first send an integer with the stream size and then the stream content. The receiver will then first receive the length and then know how many byte it has to receive for the content.

 

Also, you don't need to decode the JPEG before sending. Just load the file into the memory stream. The receiver will decode the JPEG. This will result in much less data sent thru the network.

 

I don't know why you want to send the image using a bare socket. Bette to use a higher level component such as HTTP of FTP.

 

Since you have not explained your whole problem, I cannot help you more.

 

Share this post


Link to post

As François has said, to send binary files you should really be using a high level protocol like HTTP or FTP. 

 

If you really want to invent your own protocol, you will save a lot of time and effort by using the newish TIcsIpStrmLog component that can be configured as a client or server, and is really a much easier to use version of WSocket.  There is a sample application OverbyteIcsIpStmLogTst.dpr that has a local mode, where the sample runs as both a client and server sending and receiving lines of data to itself. 

 

The component has a simple method SendStream that sends a stream of any size read from memory or a file, your earlier example with Send assume Windows can buffer your entire image, which will only work for smallish files.  There is a onLogRecvEvent event that returns a string with received binary data you can write to a stream, but you will need to design a protocol to know the file name or type of data being received, it will be returned in variable sized chunks. 

 

Angus

 

Share this post


Link to post

First thank you so much for your time and explanation, and sorry I didn't explain more,

All I'm trying to do send screenshot to the server (admin) , I can't use FTP or HTTP, I'm trying to do screen share program kind of RDP.

I thought if I understand how to send image I will do the rest, as I have code on how to capture scree and save to the MemStream

Now I understand your point,

I guess you mea something like this:

 

 Client side: (Sender)

        fstream := TMemoryStream.Create;
        try

         WSocket1.SendStr(inttostr(fstream.Size));
         wsocket1.Send(fstream.Memory, fstream.Size);
         finally
          fstream.Free;
        end;

 

Server (Receiver), as you said, I guess I have problem below.

 

procedure TTcpSrvForm.ClientDataAvailable(
    Sender : TObject;
    Error  : Word);
var
    MyStream: TMemoryStream;
    Buf : array [0..1024] of AnsiChar;
    Len : Integer;

begin

 

    Len := TCustomLineWSocket(Sender).Receive(@Buf, Sizeof(Buf) - 1);
    if Len <= 0 then
        Exit;
    Buf[Len] := #0;


    MyStream := TMemoryStream.Create;
    MyStream.WriteBuffer(Buf, Len);

 

end;

 

Thank you for any advice or suggestion.

 

Share this post


Link to post

Why not use HTTP here?

I started some kind of this stuff and got it working but postponed for production.

ICS HTTP server

procedure TControlServer.ProcessRequest(Client: THttpConnectionEx; SendType: THttpSendType; var Flags: THttpGetFlag);
begin
           ...
           if AppStatus.FormImg = nil then
           begin
             Client.AnswerHeader(Flags, HTTP_Status_204, HTTP_Type_TxtHtml, '');
             Exit;
           end;
           Client.DocStream := TMemoryStream.Create;
           Client.DocStream.CopyFrom(AppStatus.FormImg, 0);
           Client.DocStream.Position := 0;
           Client.AnswerStream(Flags, HTTP_Status_200, 'image/bmp', Client.DoContentEncoding);
         end;
...
enf;

Bmp because screenshot is in bmp, just to not deal with encoding.

With this code I got an image of main form available even in any browser

Share this post


Link to post

I don't know if possible to use HTTP for RDP, client send screen, the admin send mouse and keyboard inputs , I'm just stuck with sending and reading TMemoryStream,

Thank you for any advice or suggestion.

 

Edited by Lindawb

Share this post


Link to post

Buf should be an array of byte.

Also should be big enough to grab your image and get rid of the additional #0.

But like the other have pointed out, this will only work if it's all your sending.

Really, you should make a packet header with a fixed known size that should at least let you know the size of the additional data to recv.

Then, you just recv packet header, see data size and keep receiving unit you get it all.

 

 

i do something like this..

 

pPacketHdr=^tPacketHdr;

tPacketHdr= packer record

  Indent:array[0..15] of byte;//indentifier

  Command:byte;

  DataSize:integer;

end;

 

 

i use tByteDynArray as my final buffer..

 

outBuff:tByteDynArray;

TotalSize:=SizeOf(tPacketHdr)+MemStrm.Size;

SetLength(outBuff,TotalSize);

offSet:=0;

Move(packetHdr,outBuff[offSet],SizeOf(tPacketHdr));

offSet:=offSet+SizeOf(tPacketHdr);

then move the rest in..

 

 

 

first get the header with 0 data moving, then just add data, any data.. use command byte to tell you what your getting.. ident is unique to you and you can set and check it on every packet, if it doesn't match your ident ignore it..

Share this post


Link to post

Thank you so much for your time, I will work on this and try to understand how , where to use those and how to fill in as I said I'm not that good developer, but getting there 🙂

there are many good examples in ICS, but I can't find any stream example not even on the search engines.

Thanks again for your time.

 

Share this post


Link to post

no worries, your close, just remember, it's not a string or anistring, it's bytes.

 


type
  TMyClient = class(TWSocketThrdClient)
  public
    Buff        : array[0..9999999] of byte;
    Count:integer;
    ConnectTime : TDateTime;
    ClearToSend :boolean;
    GoodHeader  :boolean;

  end;

 

 

procedure TServerCommsDm.SocketDataAvailable(
    Sender : TObject;
    Error  : Word);
var
    Cli : TMyClient;
    Len:integer;
    aPacketHdr:TPacketHdr;
begin
    Cli := Sender as TMyClient;
    //recv bin data into our buffer..
    Len:=Cli.Receive(@Cli.Buff[Cli.Count],SizeOf(Cli.Buff)-Cli.Count);

    //did we get some!!
    if Len <= 0 then
        Exit;

        //count it..
        Cli.Count:=Cli.Count +Len;
       //see if we got enough for a packet
     if Cli.Count>=SizeOf(TPacketHdr) then
        begin
        Move(Cli.Buff,aPacketHdr,SizeOf(aPacketHdr));
        if CheckIdent(aPacketHdr.PacketIdent) then
         begin
         Cli.GoodHeader:=true;
         Display('Recvd Valid Header:DataSize='+IntToStr(aPacketHdr.DataSize));
          //packets can have extra data.. check for a datasize..
          if Cli.Count>=(aPacketHdr.DataSize+SizeOf(aPacketHdr)) then
           begin
            Display('Received packet from ' + Cli.GetPeerAddr);
            ProcessData(Cli);
           end;
         end else
            begin
              Cli.GoodHeader:=false;
              Display('Received bad header from '+Cli.GetPeerAddr);
              Cli.Count:=0;//start it all again
              FillChar(Cli.Buff,SizeOf(Cli.Buff),#0);//zero the buffer
            end;
        end;

end;

 

 

 

Share this post


Link to post

Thank you so much, I really appreciate your time and support, without people like you new developers never learn :)

you all awesome!

any idea where is TWSocketThrdClient  located on which uint ?

 

Thanks again

Share this post


Link to post

Winapi.Windows, Winapi.Messages,System.Types,System.SysUtils, System.Classes, System.SyncObjs,
OverbyteIcsWndControl, OverbyteIcsWSocket,OverbyteIcsWSocketS, OverbyteIcsWSocketTS,
OverbyteIcsUtils;

 

in one of these units.. 🙂

Share this post


Link to post

i was using the threaded server, you can probably omit the Thrd and try TWSocketClient, pretty sure it works the same just not threaded..

Share this post


Link to post

The onDataAvailable event is called repeated as more data arrives, you need to keep adding data to the receive stream until the connection is closed or your protocol signals the stream has received all that intended (why there are headers in HTTP).  

 

TWSocketThrdClient and TWSocketClient relate to ICS server components and won't help in this case. 

 

Angus

 

Share this post


Link to post

yep, and keeps adding to the client buffer and updates count, once all is recvd i processes it.

works well for me..

sorry..

Share this post


Link to post

I still recommend HTTP. You'll have to invent your own protocol anyway so why not use already implemented one. Low-level Data events and buffer plays will be hidden from you by higher level events and classes.

Share this post


Link to post

Thank you all,

I'm not that processional developer in Delphi , I thought may be someone can help me with piece of code send and receive stream, I'm kind of show-me developer :(

I will keep searching hope will find it

Thanks to all of you.

Share this post


Link to post

Yes, it's a bit tricky and Rome was not built in a day.. 🙂

thought we be sending jpegs..

 

but you should be able to bend this to your will..

 

Custom Packets

 

Good Luck!

Share this post


Link to post

Thank you so much, I really appreciate your time and help,

 

Thank you again

Edited by Lindawb

Share this post


Link to post

compiled with berlin 10.1 and ics version 8.62 Revised: August 6, 2019.

i used Getit inside berlin to download the ics.

try the latest version.

Share this post


Link to post

You guys are awesome, thanks to all of you , especially qubits posting the complete code, I found latest ICS, installed and your example worked like charm :)

Thank you all again.

Share this post


Link to post

You're welcome, was a nice break from my robot wars.

Which by the way, I used the demo ics packet server to test my cross platform packet client.

Put a working demo of this up, should also compile for Berlin.

Does the same, but pic recv is used as texture for cube which you can spin around, so it's like cooler. 🙂

TJpegImage doesn't exist in FMX, this shows how to save a jpeg stream.

 

Caution, did demo in my crazy 3d world, looking at the code too long may have adverse side affects.

robots-ics

 

have fun!

~q

 

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×