【问题标题】:Indy10, 400ms for a simple TCP/IP request and responseIndy10,一个简单的 TCP/IP 请求和响应需要 400 毫秒
【发布时间】:2013-04-07 07:17:40
【问题描述】:

我不明白为什么一个简单的请求和响应需要 400 毫秒才能完成。它只需要不到 1 毫秒就可以在 localhost(环回)上完成。当我从我的虚拟机向我的主开发机器发出请求时,需要 400 毫秒才能完成。最多需要 40 毫秒。这是一个 HTTP 请求的最大值,所以 TCP 应该更快。这是客户端和服务器的代码。我只是看不到我在哪里浪费时间。如果您需要更多信息,我可以提供个人资料。

代码与 Indy 9 和 10 兼容,这就是 IFDEF-s 的原因。并且连接已经建立,没有连接部分需要400毫秒,只有数据发送和响应。

function TIMCClient.ExecuteConnectedRequest(const Request: IMessageData): IMessageData;
var
  DataLength: Int64;
  FullDataSize: Int64;
  IDAsBytes: TIdBytes;
  IDAsString: ustring;
begin
  Result := AcquireIMCData;
  FAnswerValid := False;

  with FTCPClient{$IFNDEF Indy9}.IOHandler{$ENDIF} do
  begin
    Request.Data.Storage.Seek(0, soFromBeginning);
    DataLength := Length(Request.ID) * SizeOf(uchar);
    FullDataSize := DataLength + Request.Data.Storage.Size + 2 * SizeOf(Int64);

    SetLength(IDAsBytes, DataLength);
    Move(Request.ID[1], IDAsBytes[0], DataLength);

    // write data
    {$IFDEF Indy9}WriteInteger{$ELSE}Write{$ENDIF}(FullDataSize);
    {$IFDEF Indy9}WriteInteger{$ELSE}Write{$ENDIF}(DataLength);
    {$IFDEF Indy9}WriteBuffer{$ELSE}Write{$ENDIF}(IDAsBytes{$IFDEF Indy9}[0]{$ENDIF}, DataLength);
    {$IFDEF Indy9}WriteInteger{$ELSE}Write{$ENDIF}(Request.Data.Storage.Size);
    {$IFDEF Indy9}WriteStream{$ELSE}Write{$ENDIF}(Request.Data.Storage);

    // set the read timeout
    ReadTimeout := FExecuteTimeout;
    FullDataSize := ReadInt(FTCPClient);

    // read the message ID
    SetLength(IDAsBytes, 0);
    DataLength := ReadInt(FTCPClient);
    ReadBuff(FTCPClient, DataLength, IDAsBytes);

    if DataLength > 0 then
    begin
      SetLength(IDAsString, DataLength div SizeOf(uchar));
      Move(IDAsBytes[0], IDAsString[1], DataLength);
      Result.ID := IDAsString;
    end;

    // read the message data
    DataLength := ReadInt(FTCPClient);
    ReadStream(Result.Data.Storage, DataLength, False);
    Result.Data.Storage.Seek(0, soFromBeginning);

    // we were succesfull
    FAnswerValid := True;
  end;
end;

服务器端:

procedure TIMCServer.OnServerExecute(AContext: TIMCContext);
var
  Request: IMessageData;
  Response: IMessageData;
  DataLength: Int64;
  FullDataSize: Int64;
  IDAsBytes: TIdBytes;
  IDAsString: ustring;
begin
  with AContext.Connection{$IFNDEF Indy9}.IOHandler{$ENDIF} do
  begin
    ReadTimeout := FExecuteTimeout;
    //read the data length of the comming response
    FullDataSize := ReadInt(AContext.Connection);

    // Acquire the data objects
    Request := AcquireIMCData;
    Response := AcquireIMCData;

    // read the message ID
    DataLength := ReadInt(AContext.Connection);
    ReadBuff(AContext.Connection, DataLength, IDAsBytes);

    if DataLength > 0 then
    begin
      SetLength(IDAsString, DataLength div SizeOf(uchar));
      Move(IDAsBytes[0], IDAsString[1], DataLength);
      Request.ID := IDAsString;
    end;

    // read the message data
    DataLength := ReadInt(AContext.Connection);
    ReadStream(Request.Data.Storage, DataLength, False);

    Request.Data.Storage.Seek(0, soFromBeginning);
    try
      // execute the actual request handler
      FOnExecuteRequest(Request, Response);
    finally
      // write the data stream to TCP
      Response.Data.Storage.Seek(0, soFromBeginning);
      DataLength := Length(Response.ID) * SizeOf(uchar);
      FullDataSize := DataLength + Response.Data.Storage.Size + 2 * SizeOf(Int64);

      // write ID as binary data
      SetLength(IDAsBytes, DataLength);
      Move(Response.ID[1], IDAsBytes[0], DataLength);

      // write data
      {$IFDEF Indy9}WriteInteger{$ELSE}Write{$ENDIF}(FullDataSize);
      {$IFDEF Indy9}WriteInteger{$ELSE}Write{$ENDIF}(DataLength);
      {$IFDEF Indy9}WriteBuffer{$ELSE}Write{$ENDIF}(IDAsBytes{$IFDEF Indy9}[0]{$ENDIF}, DataLength);
      {$IFDEF Indy9}WriteInteger{$ELSE}Write{$ENDIF}(Response.Data.Storage.Size);
      {$IFDEF Indy9}WriteStream{$ELSE}Write{$ENDIF}(Response.Data.Storage);
    end;
  end;

我的代码的一位用户报告了同样缓慢的通信。他还从虚拟机测试到物理机。

更新:

以下代码在相同的两台机器之间在 2-3 毫秒内执行。它的 Indy10,最小的可能情况。

procedure TForm2.Button1Click(Sender: TObject);
var
  MyVar: Int64;
begin
  TCPClient.Host := Edit1.Text;
  TCPClient.Port := StrToInt(Edit2.Text);
  TCPClient.Connect;
  try
    stopwatch := TStopWatch.StartNew;
    MyVar := 10;
    TCPClient.IOHandler.Write(MyVar);
    TCPClient.IOHandler.ReadInt64;
    stopwatch.Stop;
    Caption := IntToStr(stopwatch.ElapsedMilliseconds) + ' ms';
  finally
    TCPClient.Disconnect;
  end;
end;

procedure TForm2.TCPServerExecute(AContext: TIdContext);
var
  MyVar: Int64;
begin
  if AContext.Connection.IOHandler.InputBuffer.Size > 0 then
  begin
    MyVar := 10;
    AContext.Connection.IOHandler.ReadInt64;
    AContext.Connection.IOHandler.Write(MyVar);
  end;

结束;

【问题讨论】:

  • 你应该重新考虑服务器端的 try..finally 块
  • @Sir Rufo,我完全同意。这会改变,因为它远非好:) 但对于我的问题,它是无关紧要的,因为它不会导致延迟。
  • 您是否也使用基于 Indy HTTP 服务器的测试应用检查了 40 毫秒的 HTTP 响应时间,还是使用不同的 HTTP 服务器实现?
  • mjn,它是来自我的另一个应用程序之一的部分独立部分突触。我稍后会检查并报告。将做一个简单的 Indy only http请求/响应
  • 您似乎遇到了与此处所述类似的问题:stackoverflow.com/questions/605580/… 所以基本上您可以将数据序列化为一个调用(发送一个大缓冲区)并禁用 nagle(设置 TCP_NODELAY)。 Nagle 默认开启。

标签: performance delphi tcp indy10


【解决方案1】:

解决了这个问题。当您找出要做什么以及问题出在哪里时,这很容易。 Indy 根本没有立即发送我的数据。我必须添加

OpenWriteBuffer
CloseWriteBuffer

调用让 Indy 在我需要时发送数据。对内部运作的简单误解会带来很多麻烦。也许这会节省一些时间。

当缓冲区关闭时,立即发送数据!

【讨论】:

  • 当写缓冲不活动时,数据直接发送到套接字,所以这种情况下唯一的延迟将是由于 Nagle。
猜你喜欢
  • 1970-01-01
  • 2020-07-19
  • 1970-01-01
  • 2016-10-04
  • 1970-01-01
  • 1970-01-01
  • 2022-06-13
  • 2017-09-25
  • 1970-01-01
相关资源
最近更新 更多