delphi 如何设置 tcpserver 来接收字符串数据
delphi how to setup tcpserver to receive string data
我必须设置一个 tcp 服务来处理一些客户端请求
所有请求均以十六进制字符串形式发送,长度为 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:客户端不断向服务器发送数据,我的意思是客户端从第3方接收数据并发送到服务器,因此数据可能不是从数据包的开头开始的!所以我必须先丢弃一些数据才能到达数据包 00D0
!
Indy 在 IdGlobal
单元中有一个 BytesToString()
函数,因此您不需要手动将 TIdBytes
转换为 string
:
RStr := BytesToString(data);
A 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()
class 方法,或 Indy 的 TIdNotify
或 TIdSync
classes 等。当您访问 UI 从主 UI 线程外部进行控制。
UPDATE:在评论中,您说数据实际上是以字节为单位,而不是文本字符。并且您需要在开始读取记录之前删除字节。在这种情况下,您根本不应将字节转换为 string
。改为按原样处理字节,例如:
procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
var
data: TIdBytes;
b: Byte;
begin
b := AContext.Connection.IOHandler.ReadByte;
repeat
if b <> [=16=] then Exit;
b := AContext.Connection.IOHandler.ReadByte;
until b = $D0;
SetLength(data, 2);
data[0] = [=16=];
data[1] = $D0;
AContext.Connection.IOHandler.ReadBytes(data, 1097, True);
repeat
if {(PWord(@data[0])^ = $D000)}(data[0] = [=16=]) and (data[1] = $D0)
and (PUInt32(@data[1091])^ = [=16=]000000) 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;
我必须设置一个 tcp 服务来处理一些客户端请求
所有请求均以十六进制字符串形式发送,长度为 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:客户端不断向服务器发送数据,我的意思是客户端从第3方接收数据并发送到服务器,因此数据可能不是从数据包的开头开始的!所以我必须先丢弃一些数据才能到达数据包 00D0
!
Indy 在 IdGlobal
单元中有一个 BytesToString()
函数,因此您不需要手动将 TIdBytes
转换为 string
:
RStr := BytesToString(data);
A 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()
class 方法,或 Indy 的 TIdNotify
或 TIdSync
classes 等。当您访问 UI 从主 UI 线程外部进行控制。
UPDATE:在评论中,您说数据实际上是以字节为单位,而不是文本字符。并且您需要在开始读取记录之前删除字节。在这种情况下,您根本不应将字节转换为 string
。改为按原样处理字节,例如:
procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
var
data: TIdBytes;
b: Byte;
begin
b := AContext.Connection.IOHandler.ReadByte;
repeat
if b <> [=16=] then Exit;
b := AContext.Connection.IOHandler.ReadByte;
until b = $D0;
SetLength(data, 2);
data[0] = [=16=];
data[1] = $D0;
AContext.Connection.IOHandler.ReadBytes(data, 1097, True);
repeat
if {(PWord(@data[0])^ = $D000)}(data[0] = [=16=]) and (data[1] = $D0)
and (PUInt32(@data[1091])^ = [=16=]000000) 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;