【问题标题】:send and recive TStringStream with indyTcp server and client使用 indy Tcp 服务器和客户端发送和接收 TStringStream
【发布时间】:2016-09-12 10:44:15
【问题描述】:

我正在尝试将TStringStream 从客户端发送到服务器,然后使用 Indy TCP 组件将其从服务器发送回客户端。

这是我的客户端代码:

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;

我可以接收TStringStream 数据,但接收到的数据已损坏,有时它会被我发送的第二个参数'IMGID5423' + sep 替换。我不确定这是否是因为通过 TCP 发送数据包的一些限制,所以数据没有完整到达,或者这是一个解析器问题?

我当前的解析器应该分隔以#13#10 结尾的每个文本。这是它的外观:

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;

我仍然对真正的问题感到困惑。我尝试在本地程序中发送TStringStream,它可以正常接收,没有任何损坏。

我通过 Indy 发送的数据是否完全错误?

这就是我接收数据的方式:

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;

【问题讨论】:

  • 我看到很多这样的代码都有一个吞下异常的异常处理程序。为什么要这么做?您不认为对异常做出反应而不是忽略它们并希望您的程序仍然有效很重要吗?
  • 我将异常记录在磁盘上,因为不确定
  • 您的编辑破坏了这个问题。我把它还原了。
  • 您可以直接发送 jpgstream,不需要 TStringStream。 Indy SendStream 将自动为您处理发送流长度
  • @whosrdaddy 除非您想维护文本通信以方便调试

标签: delphi indy indy10


【解决方案1】:

问题是您将 JPG 二进制数据保存到 TStringStream,然后让它重新解释二进制数据,就好像它是字符串数据一样。你不能那样做。您需要将 JPG 数据保存为二进制流,例如 TMemoryStream,然后使用字符串安全编码(例如 Base64)encode 二进制数据。

尝试类似的方法:

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;

然后在接收端:

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;

【讨论】:

  • 我不应该编码strcams 吗?我在这里使用了您的 base64 编码代码stackoverflow.com/questions/22026825/… 我使用了ByteEncoding 我尝试发送与您相同的内容,但图像为空白我将使用接收代码更新问题
  • 您不需要对 strcams 本身进行 base64 编码(如果您愿意,您可以,但您不需要需要) .你的整个协议都是基于字符串的,strcams 只包含 ASCII 字符,不需要特殊的编码。至于您链接到的早期代码,它用于 base64 编码 字符串数据 (通过将字符串字符转换为字符集编码的字节,然后对这些字节进行 base64 编码)。 JPG 是 二进制数据。如果您想对二进制数据进行base64编码,请使用EncodeStream()而不是EncodeString()
  • 好的,我会试试我应该解码那个编码流吗?我的意思是我在服务器上收到strcams 后正在解码
  • @Vlark.Lopin:是的,当然你必须解码strcams 内部的base64。我在答案中添加了一个示例。
  • 奇怪的是我使用了完全相同的代码,但我收到空白图像我应该从发送命令到服务器中删除 utf8 编码吗?
【解决方案2】:

您的问题似乎是您将二进制数据视为文本。二进制数据可以包含任何内容,例如 #13#10 换行符或任何内容。

如果您希望将该数据作为文本发送,则需要使用文本编码。例如,将其编码为 base64。

或者以二进制而不是文本的形式传输内容。

【讨论】:

  • 我尝试使用 base64 对二进制数据进行编码。我收到了空白图片
  • 那你做错了。不要指望我们调试您的后续代码。
  • 我试图比较发送的文本和收到的文本它们不同,我认为我遇到了编码问题
  • 毫无疑问。使用内置编码器。 Emba 或 Indy。
  • 不,您正在进行一些例行调试。证明您可以发送和接收相同的文本。证明您可以对该文本进行编码和解码。无需传输任何东西来测试它。很好地考虑您的代码,以便您可以随意调用不同的阶段。不要只是将所有代码捆绑到一个函数中。请务必尝试了解本网站。这是一个问答网站。我们不是来为你做你的工作的。
猜你喜欢
  • 2023-04-03
  • 1970-01-01
  • 2013-11-22
  • 2020-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-04
  • 2016-09-03
相关资源
最近更新 更多