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
!
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;