delphi如何设置tcpserver接收字符串数据

时间:2019-04-04 17:46:19

标签: delphi tcp indy tcplistener

我必须设置一个tcp服务来处理一些客户端请求

所有请求均以长度为1099字节的十六进制字符串包的形式发送,并且所有请求均以Hello World from Webpacker开头并以00D0结束

00000000

服务器接收到1099字节数据包,但仅显示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;

我的代码有什么问题!?

PS:客户端正在不断向服务器发送数据,这意味着客户端从第三方接收数据并发送到服务器,因此可能无法从数据包的第一位开始发送数据!所以我必须先丢弃一些数据才能首先到达数据包error invalid Frame ...

1 个答案:

答案 0 :(得分:7)

Indy在BytesToString()单元中具有IdGlobal函数,因此您无需手动将TIdBytes转换为string

RStr := BytesToString(data);

string通常是1索引的(除非您正在为移动设备编译并且不使用{$ZEROBASEDSTRINGS OFF}),所以copy(RStr,1091,8)应该使用1092而不是1091,因为读取1099字节而不是1098字节:

copy(RStr,1092,8)

但是,Indy在TextStartsWith()单元中也具有TextEndsWith()IdGlobal函数,因此您不需要手动提取和比较子字符串:

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

现在,要说的是,如果套接字数据实际上实际上是文本数据而不是二进制数据,则应使用TIdIOHandler.ReadString()方法而不是TIdIOHandler.ReadBytes()方法:

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

或者,TIdIOHandler还具有WaitFor()ReadLn()方法来读取分隔的文本,例如:

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

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

最后,TIdTCPServer是一个多线程组件,其OnExecute事件是在辅助线程而不是主UI线程的上下文中触发的。因此,访问UI时,您必须与主UI线程同步,例如通过RTL的TThread.Queue()TThread.Synchronize()类方法,或Indy的TIdNotifyTIdSync类,等。当您从主UI线程外部访问UI控件时,会发生 且通常会发生 的坏事。

更新:在注释中,您说的是数据实际上是以字节为单位,而不是文本字符。而且,在开始读取记录之前,需要删除字节。在这种情况下,您根本不应该将字节转换为string。而是按原样处理字节,例如:

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;