Search code examples
delphitcpindytcplistener

delphi how to setup tcpserver to receive string data


i have to setup a tcp service to handle some clients request

all requests are come in Hex string packets with length of 1099 byte and also all start with 00D0 and end with 00000000

procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
begin
  AContext.Connection.IOHandler.ReadBytes(data, 1099, False);

    RStr:='';
    for I := 0 to length(data)-1 do
      RStr := RStr + chr(data[i]);

    if(copy(RStr,1,4)='00D0') and (copy(RStr,1091,8)='00000000') then
      begin
       Memo14.Lines.Add( 'Frame added to database.' );
      end
    else
      begin
       Memo14.Lines.Add( 'error invalid Frame ...' );
      end;
end;

the server receive data 1099 byte packets but just error invalid Frame ... showing.

what is my code problem !?

PS: the client is sending data to server continuously,i mean client receive data from 3rd party and send to server, so its possible to data is not started from first of a packet! so i must drop some data to reach packet first 00D0!


Solution

  • Indy has a BytesToString() function, in the IdGlobal unit, so you don't need to convert a TIdBytes to a string manually:

    RStr := BytesToString(data);
    

    A string is typically 1-indexed (unless you are compiling for mobile and don't use {$ZEROBASEDSTRINGS OFF}), so copy(RStr,1091,8) should be using 1092 instead of 1091, since you are reading 1099 bytes instead of 1098 bytes:

    copy(RStr,1092,8)
    

    But, Indy has TextStartsWith() and TextEndsWith() functions, also in the IdGlobal unit, so you don't need to extract and compare substrings manually:

    if TextStartsWith(RStr, '00D0') and TextEndsWith(RStr, '00000000') then
    

    Now, that being said, if your socket data really is textual in nature and not binary, you should use the TIdIOHandler.ReadString() method instead of the TIdIOHandler.ReadBytes() method:

    RStr := AContext.Connection.IOHandler.ReadString(1099);
    

    Alternatively, TIdIOHandler also has WaitFor() and ReadLn() methods for reading delimited text, eg:

    AContext.Connection.IOHandler.WaitFor('00D0');
    RStr := '00D0' + AContext.Connection.IOHandler.ReadLn('00000000') + '00000000';
    

    or

    AContext.Connection.IOHandler.WaitFor('00D0');
    RStr := '00D0' + AContext.Connection.IOHandler.WaitFor('00000000', True, True);
    

    Lastly, TIdTCPServer is a multithreaded componnet, its OnExecute event is fired in the context of a worker thread, not the main UI thread. As such, you MUST sync with the main UI thread when accessing the UI, such as via the RTL's TThread.Queue() or TThread.Synchronize() class methods, or Indy's TIdNotify or TIdSync classes, etc. Bad things can and usually do happen when you access UI controls from outside of the main UI thread.

    UPDATE: in comments, you say the data is actually in bytes, not text characters. And that you need to drop bytes before you start reading records. In that case, you should not be converting the bytes to a string at all. Handle the bytes as-is instead, eg:

    procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
    var
      data: TIdBytes;
      b: Byte;
    begin
      b := AContext.Connection.IOHandler.ReadByte;
      repeat
        if b <> $00 then Exit;
        b := AContext.Connection.IOHandler.ReadByte;
      until b = $D0;
    
      SetLength(data, 2);
      data[0] = $00;
      data[1] = $D0;
      AContext.Connection.IOHandler.ReadBytes(data, 1097, True);
    
      repeat
        if {(PWord(@data[0])^ = $D000)}(data[0] = $00) and (data[1] = $D0)
          and (PUInt32(@data[1091])^ = $00000000) then
        begin
          TThread.Queue(nil,
            procedure
            begin
              Memo14.Lines.Add( 'Frame added to database.' );
            end;
          );
        end else
        begin
          TThread.Queue(nil,
            procedure
            begin
              Memo14.Lines.Add( 'error invalid Frame ...' );
            end;
          );
        end;
        AContext.Connection.IOHandler.ReadBytes(data, 1099, False);
      until False;
    end;