send and recive TStringStream with indyTcp server and client

浪子不回头ぞ 提交于 2019-12-25 18:27:08

问题


I am trying to send a TStringStream from client to server, then send it back from server to client, using Indy TCP components.

Here is my client code:

var
  Jpg: TJPEGImage;
  StringStream: TStringStream;
  strcams, StringImageData: String;
  byt, i: integer;

procedure SendCommandWithParams(Command, Params: String);
begin
  Lock;
  try
    if not FTCP.Connected then
    begin
      exit;
    end;
    FTCP.Socket.WriteLn('1' + Command, IndyTextEncoding_UTF8);
    FTCP.Socket.WriteLn(Params, IndyTextEncoding_UTF8);
  finally
    Unlock;
  end;
end;

begin
  Jpg := TJPEGImage.Create;
  StringStream := TStringStream.Create('');
  try
    try
      Jpg.Performance := jpBestSpeed;
      Jpg.ProgressiveEncoding := True;
      Jpg.ProgressiveDisplay := True;
      Jpg.Assign(Image2.Picture.Bitmap);
      Jpg.CompressionQuality := 25;
      Jpg.Compress;
      Jpg.SaveToStream(StringStream);
      StringImageData := StringStream.DataString;
      strcams := '<[S:' + IntToStr(Length(StringImageData)) + 'B]>' +
        StringImageData;
      if Length(strcams) < byt then
      begin
       SendCommandWithParams('SIMGSEND', strcams + sep + 'IMGID5423' + sep);
      end;
    except
      on e: exception do
        //
    end;
  finally
    StringImageData := '';
    FreeAndNil(Jpg);
    FreeAndNil(StringStream);
  end;
end;

I can receive the TStringStream data, but the data received is corrupted, and some times it gets replaced with the second parameter that I send which is 'IMGID5423' + sep. I am not sure if this is because of some limit of packet sending through TCP so the data does not arrive complete, or is this a parser issue?

My current parser should separate each text that ended with #13#10. Here is how it looks:

var
  ReceiveParams, ReceiveStream: Boolean;
  S: string;
  Command: String;
begin
  Command := Fholdcommand;
  ReceiveParams := false;
  ReceiveStream := false;

  if Command[1] = '1' then // command with params
  begin
    Command := Copy(Command, 2, MaxInt);
    ReceiveParams := True;
  end;

  if ReceiveParams then // params incomming
  begin
    S := FTCP.Socket.ReadLn(IndyTextEncoding_UTF8);
    FCMD := Command;
    FPRMS := S;
    FSTREAM := false;
    if Assigned(FOnCallbackProc) then
    begin
      Synchronize(DoCallbackProc);
    end;
  end;

I am still confused about the real issue. I try to send the TStringStream in a local procedure, and it is received normally without any corruption.

Am I sending the data wrong altogether through Indy?

This is how I am receiving the data:

procedure CreateJpg(Data:string);
var
  StringStream : TStringStream;
  JpegImage : TJPEGImage;
  Bitmap : TBitmap;
  tmpPos:integer;
  pp:string;
  label check;
begin
  GData := Data;

  if LeftStr(GData,4) = '<[S:' then 
  begin
    tmpPos := Pos(WideString('B]>'),GData);
    pp := Copy(GData,5,tmpPos-5);
    CDataELen :=  StrToInt(pp); //MidStr(st,5,tmppos - 5);
    CData := RightStr(GData,length(GData)-(tmppos+2));
    goto check;
  end;

  CData := CData + GData;

  check:

  //if CDataELen = length(CData) then
  begin
    StringStream := TStringStream.Create('');
    JpegImage := TJpegImage.Create;
    StringStream.WriteString(CData);
    CData := '';
    try
      try
        StringStream.Seek(0, soFromBeginning);
        JpegImage.LoadFromStream(StringStream);
        Bitmap := TBitmap.Create;
        with Bitmap do
        begin
          Canvas.Lock;
          try
            Width := JpegImage.Width;
            Height := JpegImage.Height;
            Canvas.Draw(0, 0, JpegImage);
          finally
            Canvas.Unlock;
          end;
        end;

        img.Picture.Bitmap.Width := Bitmap.Width;
        img.Picture.Bitmap.Height := Bitmap.Height;
        img.Picture.Bitmap.Canvas.Draw(0, 0, Bitmap);

      except
        on E: Exception do
          //
      end;
    finally
      FreeAndNil(StringStream);
      FreeAndNil(JpegImage);
      FreeAndNil(Bitmap);
    end;
  end;
end;

回答1:


The problem is that you are saving the JPG binary data to a TStringStream and then letting it reinterpret the binary data as if it were string data. You can't do that. You need to save the JPG data to a binary stream instead, like TMemoryStream, and then encode the binary data using a string-safe encoding, like Base64.

Try something more like this instead:

uses
  ..., IdCoder, IdCoderMIME;

...

var
  Jpg: TJPEGImage;
  JpegStream: TMemoryStream;
  strcams, StringImageData: String;
begin
  try
    JpegStream := TMemoryStream.Create;
    try
      Jpg := TJPEGImage.Create;
      try
        Jpg.Performance := jpBestSpeed;
        Jpg.ProgressiveEncoding := True;
        Jpg.ProgressiveDisplay := True;
        Jpg.Assign(Image2.Picture.Bitmap);
        Jpg.CompressionQuality := 25;
        Jpg.Compress;
        Jpg.SaveToStream(JpegStream);
      finally
        Jpg.Free;
      end;
      JpegStream.Position := 0;
      StringImageData := TIdEncoderMIME.EncodeStream(JpegStream);
    finally
      JpegStream.Free;
    end;
    strcams := '<[S:' + IntToStr(Length(StringImageData)) + 'B]>' + StringImageData;
    SendCommandWithParams('SIMGSEND', strcams + sep + 'IMGID5423' + sep);
  except
    on e: exception do
      //
  end;
end;

And then on the receiving end:

procedure CreateJpg(Data: string);
var
  JpegStream: TMemoryStream;
  JpegImage: TJPEGImage;
  Bitmap: TBitmap;
  tmpPos, tmpLen: integer;
  pp: string;
begin
  try
    if not TextStartsWith(Data, '<[S:') then
    begin
      // bad data, do something else...
      Exit;
    end;

    tmpPos := Pos('B]>', Data);
    pp := Copy(Data, 5, tmpPos-5);
    tmpLen := StrToInt(pp);
    Data := Copy(Data, tmpPos+3, tmpLen);

    Bitmap := TBitmap.Create;
    try
      JpegImage := TJpegImage.Create;
      try
        JpegStream := TMemoryStream.Create;
        try
          TIdDecoderMIME.DecodeStream(Data, JpegStream);
          JpegStream.Position := 0;
          JpegImage.LoadFromStream(JpegStream);
        finally
          JpegStream.Free;
        end;
        with Bitmap do
        begin
          Canvas.Lock;
          try
            Width := JpegImage.Width;
            Height := JpegImage.Height;
            Canvas.Draw(0, 0, JpegImage);
          finally
            Canvas.Unlock;
          end;
        end;
      finally
        JpegImage.Free;
      end;
      img.Picture.Assign(Bitmap);
    finally
      Bitmap.Free;
    end;        
  except
    on E: Exception do
      //
  end;
end;



回答2:


Your problem appears to be that you are treating binary data as though it is text. Binary data can contain anything, for instance #13#10 line breaks or indeed anything whatsoever.

If you wish to send that data as text, then you need to use a text encoding. For example, encode it as base64.

Or transmit the content as binary rather than text.



来源:https://stackoverflow.com/questions/39448538/send-and-recive-tstringstream-with-indytcp-server-and-client

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!