首页 > 解决方案 > Indy10 ConnectTimeout 最小值

问题描述

我对ConnectTimeout来自 Indy 10's 的产品有疑问TIdTCPClient

当设置ConnectTimeout高于 125ms 时,Connect()程序将阻塞当前线程 125ms。如果小于 125 毫秒,它将阻塞给定时间(例如,如果超时设置为 30 毫秒,它会阻塞 30 毫秒)。在这两种情况下,连接都是稳定的,我可以发送和接收数据。

为什么TIdTCPClient会有这样的行为?

恕我直言,该Connect()过程应在成功建立连接后直接退出,并且仅在无法打开连接时才阻止超时的全部持续时间。

这是我用于监控Connect()程序持续时间的代码。
呼叫定时器TimerConnectTimer设置为 250 毫秒。

我在 Windows 7 Professional 下使用 Lazarus v1.6.4 和 Indy 10。

procedure TForm1.FormCreate(Sender: TObject);
begin
  timer := TEpikTimer.create(self);
  timer.Clear;
end;

procedure TForm1.TimerConnectTimer(Sender: TObject);
begin
  timer.Start;
  client := TIdTCPClient.create();
  logTime(0);
  // Tested with values between 10ms and 1000ms
  client.ConnectTimeout := SpinEdit1.Value;
  try
    logTime(1);
    // choose ip and port for a running server
    client.connect('192.168.2.51', 9912);
    logTime(2);
  except

  end;

  logTime(3);

  try
    client.Disconnect();
    FreeAndNil(client);
  except

  end;

  logTime(4);
  timer.Clear;
end;

procedure TForm1.logTime(ch: integer);
begin
  StringGrid1.Cells[0, ch] := FormatFloat('0.00', timer.Elapsed*1000);
end;

标签: connectiontimeoutclientfreepascalindy10

解决方案


当设置ConnectTimeout高于 125ms 时,Connect()程序将阻塞当前线程 125ms。如果小于 125 毫秒,它将阻塞给定时间(例如,如果超时设置为 30 毫秒,它会阻塞 30 毫秒)。在这两种情况下,连接都是稳定的,我可以发送和接收数据。

为什么TIdTCPClient会有这样的行为?

如果不知道您使用的是哪个版本的 Indy 10,这很难回答,因为ConnectTimeout多年来的实施已经发生了变化。但是,总的来说:

如果ConnectTimeout设置为IdTimeoutDefaultor 0IdTimeoutInfinite则改为使用。

如果Connect()在主 UI 线程中调用,TIdAntiFreeze正在使用,并且使用了无限超时,则使用硬编码的 2 分钟超时。

如果使用了任何超时,则Connect()生成一个工作线程以将套接字连接到服务器,然后等待超时以使线程终止。如果在线程终止之前超时时间已过,Indy 将关闭套接字并终止线程。

假设您使用的是相当最新的 Indy 10 版本(至少 2016 年 12 月 14 日的 SVN 修订版 5382,或更高版本),并且没有使用TIdAntiFreeze,那么仅在 Windows 上,当工作线程终止时,等待应该WaitForSingleObject()立即退出,因为 Indy在线程上进行一次调用以获得完全超时并在退出后立即WFSO退出。

如果您正在使用TIdAntiFreeze,或者正在非 Windows 平台上使用 Indy,或者正在使用 SVN rev 5382 之前的 Indy 10 版本,则等待调用IndySleep()(如果需要,TIdAntiFreeze.DoProcess())以固定间隔(125ms,TIdAntiFreeze.IdleTimeOut或以较小者为准)直到超时或线程终止。在这种情况下,退出之前可能会有轻微的延迟,因为每个睡眠周期必须在检查线程是否已终止之前完成,并且在整个连接超时内的每个睡眠间隔都这样做。Connect()Connect()

恕我直言,该Connect()过程应在成功建立连接后直接退出,并且仅在无法打开连接时才阻止超时的全部持续时间。

这正是它目前所做的,至少在 Windows 上是这样。如果连接成功,线程终止,并Connect()立即退出(即使TIdAntiFreeze被使用)。当前的睡眠周期由终止自身的线程结束。在非 Windows 平台上情况并非如此(目前,可能会在未来的版本中解决)。

请注意,以上所有内容仅在使用超时时适用。如果不使用超时,则也不使用工作线程。 Connect()只需直接连接套接字并让尝试阻塞调用线程直到完成,无论成功与否。

这是我用于监控Connect()程序持续时间的代码。

我会建议更像这样的东西:

procedure TForm1.TimerConnectTimer(Sender: TObject);
begin
  try
    timer.Start;
    try
      logTime(0);

      client := TIdTCPClient.create();
      try
        // choose ip and port for a running server
        client.Host := '192.168.2.51';
        client.Post := 9912;

        // Tested with values between 10ms and 1000ms
        client.ConnectTimeout := SpinEdit1.Value;

        logTime(1);
        client.Connect();
        logTime(2);
        client.Disconnect();
        logTime(3);

      finally
        FreeAndNil(client);
      end;

      logTime(4);
    finally
      timer.Clear;
    end;
  except
  end;
end;

推荐阅读