Search code examples
stringsocketsdelphitcpdelphi-10.1-berlin

Losing data when transfering from Bytes Buffer to AnsiString


I am trying to send a 4063 bytes AMR file (just for testing) from my CentOS VPS

nc IP 45500 < sample.amr 

But when the application receives it just show a cutted data:

1448: #!AMR-WB
Ô_Æ ÅѤm^8E•Ì^^ìÖõ¾_€°

2617: 

The strange thing is: If I edit the AMR file and remove the marked char:

enter image description here

And send again the same AMR file I receive:

1448: #!AMR-WB
Ô_Æ ÅѤm^8E•Ì^^ìÖõ¾_€°ÀžH¯ë2Çc oÚÖɾøy$Ý

2616: ¢Khw^è“ʺ\?¬šJ£<é<'

.. more data, specifically up to the next NUL char

enter image description here

This is the code for my Socket onClientRead

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  received,a: string;
  size,i: Integer;
  AnsiStr: String;
  Bytes: array[0..2048*256] of Byte;
begin

  Size := Socket.ReceiveLength;
  Socket.ReceiveBuf(Bytes[0], Size);

  SetString(AnsiStr, PAnsiChar(@Bytes[0]), Size);
  received := IntToStr(Size)+': '+AnsiStr;

  Memo1.Lines.Add(received);
  Memo1.Lines.Add(' ');
end;

I believe the SetString command is causing this, but not sure how to receive the whole DATA. I just want to receive the whole file and copy it to a new AMR file on my side.

Extra: The file that I am trying to send from my server to my computer is located here: http://techslides.com/demos/samples/sample.amr


Solution

  • There are two main reasons why your code doesn't work:

    • you are using a Unicode version of Delphi, so the string type maps to UnicodeString, instead of AnsiString like you are expecting.

    • internally, the TMemo.Lines.Add() method passes the string to the TMemo window using the EM_REPLACESEL message, which treats the input string as null-terminated. That is why your file content gets truncated at nul "characters".

    As other people said in comments, you are trying to display binary data as if it were text, but it is not text. Save the raw binary data as-is to an actual file on your HDD instead. If you want to display the file bytes in your UI, display them in a text-safe format, such as hex.

    Try something more like this:

    procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;
      Socket: TCustomWinSocket);
    begin
      Socket.Data := TFileStream.Create('<some path>\sample.amr', fmCreate);
    end;
    
    procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject;
      Socket: TCustomWinSocket);
    begin
      TFileStream(Socket.Data).Free;
    end;
    
    procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
      Socket: TCustomWinSocket);
    var
      Size: Integer;
      Bytes: TBytes;
      Received: string;
    begin
      Size := Socket.ReceiveLength;
      if Size < 1 then Exit;
    
      SetLength(Bytes, Size);
      Size := Socket.ReceiveBuf(Bytes[0], Size);
      if Size < 1 then Exit;
    
      TFileStream(Socket.Data).WriteBuffer(Bytes[0], Size);
    
      SetLength(Received, Size*2);
      BinToHex(Bytes[0], PChar(Received), Size);
    
      Memo1.Lines.Add(IntToStr(Size) + ': ' + Received);
      Memo1.Lines.Add(' ');
    end;