首页 > 解决方案 > delphi如何设置tcpserver来接收字符串数据

问题描述

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

所有请求都以十六进制字符串数据包的形式出现,长度为 1099 字节,并且都以 1099 字节开头00D0和结尾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;

服务器接收数据 1099 字节数据包,但只是error invalid Frame ...显示。

我的代码问题是什么!?

PS:客户端不断向服务器发送数据,我的意思是客户端从第三方接收数据并发送到服务器,所以它可能不是从数据包的第一个开始数据!所以我必须先丢弃一些数据才能到达数据包00D0

标签: delphitcpindytcplistener

解决方案


Indy 有一个BytesToString()函数,在IdGlobal单位中,所以你不需要手动将 a 转换TIdBytes为 a :string

RStr := BytesToString(data);

Astring通常是 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 线程同步,例如通过 RTLTThread.Queue()TThread.Synchronize()类方法,或 IndyTIdNotifyTIdSync类等。当您从主 UI 外部访问 UI 控件时,可能而且通常发生坏事线。

更新:在评论中,您说数据实际上是以字节为单位的,而不是文本字符。并且您需要在开始读取记录之前删除字节。在这种情况下,您根本不应该将字节转换为 a 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;

推荐阅读