Delphi:Indy TIdTCPClient 读取 XML

Delphi: Indy TIdTCPClient Reading XML

我正在使用 TidTCPClient 打开到服务器的套接字连接。 服务器开始向我发送不同 XML 数据的音调。 但是 ReadLn 对我没有帮助,因为我不知道 xml 在哪里开始和停止。所以我决定使用 Capture。这工作正常。但正如我从文档中读到的那样,捕获也调用了 ReadLn,这意味着它逐行读取。 30~40 行 XML 有时需要 1~2 秒才能阅读。所以我需要更快地阅读它。我可以使用 ReadStream 或任何其他有区别的东西吗?

我现在使用的代码:

procedure TfrmMain.ThreadRun(Sender: TIdThreadComponent);
var
  FData: TStringList;
  FMemory: TStringStream;
begin
  if Client.Connected = False then Exit;
  FData := TStringList.Create;
  Client.IOHandler.Capture(FData, '', False);
  FMemory := TStringStream.Create('',TEncoding.UTF8);
  FMemory.WriteString(FData.Text);
  CoInitialize(Nil);
  QInsert.ParamByName('XMLData').LoadFromStream(FMemory,ftBlob);
  QInsert.Execute;
  CoUninitialize;
  FMemory.Free;
  FData.Free;
end;

如您所见,我正在将 XML 数据插入本地 table,这完成得非常快,15ms ~ 60ms,具体取决于 XML 的大小,但是越来越数据真的很蛋疼

Capture() 在循环中调用 ReadLn() 直到读取指定的终止符行(在您的示例中为空行)。

您需要知道每个 XML 文档的结束位置和下一个文档的开始位置。如果每个文档之间没有明显的标记(一个空行就可以,只要每个 XML 文档在空行之前以换行符结尾,并且 [=36 内部不包含任何空行=]), 那么你唯一剩下的选择就是阅读第一个文档的开始标签,继续阅读直到找到相应的结束标签,然后阅读下一个文档的开始标签并等待它的结束标签,依此类推。

在这种情况下,您最好使用支持推送模型的 XML 解析器来处理该问题。您只需从套接字中读取原始字节并将它们按原样推送到解析器中,让它在遇到每个顶级文档元素的开始和结束时告诉您。您可以缓冲解析器在这两个事件之间为您提供的任何数据,使用开始事件 clear/prepare 缓冲区和结束事件 finalize/flush 数据库缓冲区。

如果您不 willing/able 使用预先存在的 XML 库,则必须编写自己的解析器。

更新: 您展示的示例 Capture()ADelim 参数设置为 '',只有 XML 文档由 LF LFCR LF CR LF 序列分隔。在这种情况下,您可以将该定界符用作 ReadLn()ATerminator 参数并让它一次读取 1 个整个文档,而不是完全使用 Capture()

procedure TfrmMain.ThreadBeforeRun(Sender: TIdThreadComponent);
begin
  CoInitialize(Nil);
end;

procedure TfrmMain.ThreadAfterRun(Sender: TIdThreadComponent);
begin
  CoUninitialize;
end;

procedure TfrmMain.ThreadRun(Sender: TIdThreadComponent);
var
  Xml: String;
  FMemory: TStringStream;
begin
  // read until a 'CR LF CR LF' sequence is reached...
  Xml := TrimRight(Client.IOHandler.ReadLn(CR+LF+CR+LF, IndyTextEncoding_UTF8));
  // alternatively:
  // Xml := Client.IOHandler.WaitFor(CR+LF+CR+LF, True, False, IndyTextEncoding_UTF8);

  FMemory := TStringStream.Create(Xml, TEncoding.UTF8);
  try
    QInsert.ParamByName('XMLData').LoadFromStream(FMemory, ftBlob);
  finally
    FMemory.Free;
  end;

  QInsert.Execute;
end;