How to read all bytes from server using Indy Client in Delphi?

12,246

Solution 1

It's hard to answer you, unless you precisely describe what's written in documentation you have. So far we know that your 512B packet consists from 6 words and 10x50B strings. So, take this just as starting point, until you tell us more:

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  Buffer: TBytes;
  WordArray: array[0..5] of Word;
  StringArray: array[0..9] of AnsiString;
begin
  if IdTCPClient1.IOHandler.InputBufferIsEmpty then
  begin
    if IdTCPClient1.IOHandler.CheckForDataOnSource(1000) then
      IdTCPClient1.IOHandler.ReadBytes(Buffer, 512, False);

    for I := 0 to High(WordArray) do
    begin
      WordRec(WordArray[I]).Hi := Buffer[I * 2];
      WordRec(WordArray[I]).Lo := Buffer[I * 2 + 1];
    end;
    for I := 0 to High(StringArray) do
      SetString(StringArray[I], PAnsiChar(@Buffer[I * 50 + 12]), 50);

    // here you have the arrays prepared to be processed
  end;
end;

Solution 2

1: what is the charset of the string ? is it 1-byte windows-1251 ? or 2-bytes Unicode UCS-2 ? or variable-length UTF-8 or UTF-16 ?

2: what is the length of the string ? always 50 ?


reading the buffer:

  1. reading the manuals
    1.1 http://www.indyproject.org/docsite/html/TIdIOHandler_ReadBytes@TIdBytes@[email protected]
    1.2 http://www.indyproject.org/docsite/html/[email protected] 1.3 http://docwiki.embarcadero.com/Libraries/XE2/en/System.SetString
  2. making code accurately following types and parameter descriptions.
    2.1 Reading header: That should result in something like

    var  Word1, Word2: word;
    
    Word1 := IOHandler.ReadSmallInt(false);   
    Word2 := IOHandler.ReadSmallInt(false);
    
  3. reading single-byte string
    3.1 reading buffer
    3.2 converting buffer to string

    var  Word1, Word2: word;  Buffer: TIdBytes;    
    var s: RawByteString;     
     // or AnsiString; or maybe UTF8String; but probably not UnicodeString aka string
    
    Word1 := IOHandler.ReadSmallInt(false);   
    Word2 := IOHandler.ReadSmallInt(false);
    
    // You should check that you really received 50 bytes,
    // then do something like that:
    
    IOHandler.ReadBytes(Buffer, 50, false);    
    Assert(Length(Buffer)=50);    
    SetString (s, pointer(@Buffer[0]), 50);    
    
  4. Continue reading the rest - you only read 50+2+2 = 54 bytes of 512 bytes packet - there should be more data.

512 = 54*9+26 - so it might look like a loop - and discarding the 26 bytes tail.

    var Word1, Word2: word;  Buffer: TIdBytes;    
    var s: RawByteString;     

    for i := 1 to 9 do begin    
      Word1 := IOHandler.ReadSmallInt(false);     
      Word2 := IOHandler.ReadSmallInt(false);  

      IOHandler.ReadBytes(Buffer, 50, false);        
      Assert(Length(Buffer)=50);      
      SetString (s, pointer(@Buffer[0]), 50);    

      SomeOutputCollection.AppendNewElement(Word1, Word2, s);   
    end;   
    IOHandler.ReadBytes(Buffer, 512 - 9*(50+2+2), false); // discard the tail
Share:
12,246
Admin
Author by

Admin

Updated on June 11, 2022

Comments

  • Admin
    Admin almost 2 years

    I am using Indy client to read the message the server is sending to me (client). It sends 512 bytes of data to me in one go. This 512 bytes of data is composed of two datatypes (Word and String). For example, it sends Word of 2 bytes, then again Word of 2 bytes and then String of 50 bytes and so on. I am trying following code to cope with this problem:

    var BufferArray : Array[0..512] of Byte;
    
     if IdTCPClient1.IOHandler.InputBufferIsEmpty then
     begin
          if IdTCPClient1.IOHandler.CheckForDataOnSource(1000) then
          begin
              Edit1.Text := idtcpclient1.IOHandler.ReadBytes(BufferArray ,512, true);
          end;
     end;
    

    I am getting error on line Edit1.Text := idtcpclient1.IOHandler.ReadBytes(BufferArray ,512, true); Error: Type of actual and formal var parameter must be identical.

    Is it right approach I am using. I want to store whole 512 bytes on Edit1.Text and then will do whatever I want to do with that data. Please help me in getting all 512 bytes from the server.

    Update: Alternating Approach

    I am using this approach to read word and string values

    WordArray : array[0..5] of word;
    
     if IdTCPClient1.IOHandler.InputBufferIsEmpty then
     begin
          if IdTCPClient1.IOHandler.CheckForDataOnSource(1000) then
          begin
            i := 0;
            while i < 6 do //Read all the words
            begin
                //Fill WORD data in array
                WordArray[i] :=  (IdTCPClient1.Socket.ReadWord(True));
            end;
          end;
    end;
    

    Similar approach for string like

    WordArray[i] := (IdTCPClient1.Socket.ReadString(50));

    Thats working fine, but I have to remain the connection open while I read all the data in loop. If in between connection goes, I lose everything and have to request the whole package again from server.