Delphi 在读取OneExecute事件中的所有数据之前,什么可能导致IdTCPServer失败?
当我使用Indy客户端组件通过LAN发送数据时,该代码工作正常,但当我从web接收外部应用程序的数据时,它会导致数据失败。在读取所有数据之前,客户端是否会导致IdTCPServer断开连接?客户端平均发送33000个字符。有什么建议吗Delphi 在读取OneExecute事件中的所有数据之前,什么可能导致IdTCPServer失败?,delphi,tcp,indy,Delphi,Tcp,Indy,当我使用Indy客户端组件通过LAN发送数据时,该代码工作正常,但当我从web接收外部应用程序的数据时,它会导致数据失败。在读取所有数据之前,客户端是否会导致IdTCPServer断开连接?客户端平均发送33000个字符。有什么建议吗 procedure TFrmMain.IdTCPServer1Execute(AContext: TIdContext); var strm: TMemoryStream; RxBuf: TIdBytes; begin Memo1.Clear; s
procedure TFrmMain.IdTCPServer1Execute(AContext: TIdContext);
var
strm: TMemoryStream;
RxBuf: TIdBytes;
begin
Memo1.Clear;
strm := TMemoryStream.Create;
try
// read until disconnected
AContext.Connection.IOHandler.ReadStream(strm, -1, true);
strm.Position := 0;
ReadTIdBytesFromStream(strm, RxBuf, strm.Size);
finally
strm.Free;
end;
Memo1.Lines.Add(BytesToString(RxBuf));
AContext.Connection.IOHandler.WriteLn('000');
end;
我还尝试了另一个代码,与第一个代码不同,它只读取发送的部分数据。有没有办法让IdTCPServer处理程序等待所有数据收集完毕
procedure TFrmMain.IdTCPServer1Execute(AContext: TIdContext);
var
RxBuf: TIdBytes;
begin
RxBuf := nil;
with AContext.Connection.IOHandler do
begin
CheckForDataOnSource(10);
if not InputBufferIsEmpty then
begin
InputBuffer.ExtractToBytes(RxBuf);
end;
end;
AContext.Connection.IOHandler.WriteLn('000');
Memo1.Lines.Add( BytesToString(RxBuf) );
end;
为了进一步参考任何人,我必须创建一个循环并等待客户端发送EOT chr(4),以便收集IdTCPServer1Execute上的所有数据。这是因为数据被Indy分割,代码如下所示:
procedure TFrmMain.IdTCPServer1Execute(AContext: TIdContext);
var
Len: Integer;
Loop: Boolean;
begin
CoInitialize(nil);
cdsSurescripts.Close;
Loop := True;
while Loop = true do
begin
if AContext.Connection.IOHandler.Readable then
begin
AContext.Connection.IOHandler.ReadBytes( RxBuf,-1, True);
Len := Length(BytesToString(RxBuf));
if Copy(BytesToString(RxBuf), Len, 1) = '' then
begin
loop := False;
end;
end;
end;
Display('CLIENT', BytesToString(RxBuf));
AContext.Connection.IOHandler.WriteLn('000');
CoUninitialize();
end;
你作为答案发布的代码完全错误 首先,您不能在任意字节块上使用
BytesToString()
,这将无法正确处理UTF-8等多字节编码
此外,您没有正确查找EOT终止符。如果客户端发送多条XML消息,则不能保证每次读取后它将是RxBuf
的最后一个字节。即使是这样,使用Copy(BytesToString(),…)
将其提取到字符串中,也不会像代码所期望的那样产生空字符串
如果客户端在XML末尾发送EOT终止符,则不需要手动读取循环。只需使用EOT终止符调用TIdIOHandler.ReadLn()
,并让它在内部处理读取循环,直到到达EOT为止
另外,CoInitialize()
和CoUninitialize()
调用应该分别在OnConnect
和OnDisconnect
事件中完成(实际上,最好在分配给TIdSchedulerOfThread.ThreadClass
属性的TIdThreadWithTask
子代中调用它们,但这是下一次更高级的主题)
尝试类似以下内容:
procedure TFrmMain.IdTCPServer1Execute(AContext: TIdContext);
var
Len: Integer;
Loop: Boolean;
begin
CoInitialize(nil);
cdsSurescripts.Close;
Loop := True;
while Loop = true do
begin
if AContext.Connection.IOHandler.Readable then
begin
AContext.Connection.IOHandler.ReadBytes( RxBuf,-1, True);
Len := Length(BytesToString(RxBuf));
if Copy(BytesToString(RxBuf), Len, 1) = '' then
begin
loop := False;
end;
end;
end;
Display('CLIENT', BytesToString(RxBuf));
AContext.Connection.IOHandler.WriteLn('000');
CoUninitialize();
end;
过程TFrmMain.IdTCPServer1Connect(AContext:TIdContext);
开始
共初始化(零);
AContext.Connection.IOHandler.DefStringEncoding:=IndyTextEncoding\u UTF8;
结束;
过程TFrmMain.IdTCPServer1Disconnect(AContext:TIdContext);
开始
coninitialize();
结束;
过程TFrmMain.IdTCPServer1Execute(AContext:TIdContext);
变量
XML:string;
开始
CDS说明。关闭;
XML:=AContext.Connection.IOHandler.ReadLn(#4);
显示('CLIENT',XML);
AContext.Connection.IOHandler.WriteLn('000');
结束;
就个人而言,我会采取另一种方法。我建议使用支持推送模型的XML解析器。然后您可以从连接中读取任意字节块并将它们推送到解析器中,让它为完成的XML元素向您触发事件,直到到达终止符。这样,您就不必浪费时间和内存或者先在内存中缓冲整个XML,然后再处理它。…这是导致它失败的原因。
这是什么意思?你观察到什么导致了这个“失败”的结论?如果使用AContext.Connection.IOHandler.ReadStream(strm,0,true)会发生什么
?OneExecute事件在工作线程中运行,而不是在主UI线程中运行。您在不同步的情况下使用TMemo不是线程安全的。此外,如果要在断开连接之前读取客户端的所有数据,则以后调用WriteLn是没有意义的,因为客户端将永远无法获取它。根据数据的性质,readi在使用之前将所有数据放入一个大内存缓冲区可能太浪费了。数据实际上是什么样子的?当我说失败时,我会进行跟踪,当它在这一行时:AContext.Connection.IOHandler.ReadStream(strm,-1,true);它跳转到最后一个异常。@user734781…它跳转到最后一个异常
。因此…捕获异常并告诉我们它是什么。这个代码都错了。请参阅我刚才发布的答案。