Indy10 ConnectTimeout минимальное значение - PullRequest
0 голосов
/ 12 ноября 2018

У меня проблема с ConnectTimeout из Indy 10 TIdTCPClient.

При установке ConnectTimeout выше 125 мс процедура Connect() заблокирует текущий поток на 125 мс. Если оно меньше 125 мс, оно будет блокироваться на заданное время (например, оно блокируется на 30 мс, если время ожидания установлено на 30 мс). В обоих случаях соединение стабильно, и я могу передавать и получать данные.

Почему TIdTCPClient ведет себя так?

ИМХО, процедура Connect() должна завершиться сразу после успешного установления соединения и блокировать полную продолжительность тайм-аута, только если соединение не может быть открыто.

Вот мой код для контроля продолжительности процедуры Connect().
Таймер для вызова TimerConnectTimer установлен на 250 мс.

Я использую Lazarus v1.6.4 и Indy 10 под Windows 7 Professional.

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;

1 Ответ

0 голосов
/ 12 ноября 2018

При установке ConnectTimeout выше 125 мс, процедура Connect() заблокирует текущий поток на 125 мс. Если оно меньше 125 мс, оно будет блокироваться на заданное время (например, оно блокируется на 30 мс, если время ожидания установлено на 30 мс). В обоих случаях соединение стабильно, и я могу передавать и получать данные.

Почему TIdTCPClient ведет себя так?

Трудно ответить, не зная, какую именно версию Indy 10 вы используете, так как реализация ConnectTimeout менялась с годами. Но в целом:

Если для ConnectTimeout установлено значение IdTimeoutDefault или 0, вместо него используется IdTimeoutInfinite.

Если в основном потоке пользовательского интерфейса вызывается Connect(), используется TIdAntiFreeze и используется бесконечный тайм-аут, вместо него используется жестко заданный 2-минутный тайм-аут.

Если используется какое-либо время ожидания, Connect() порождает рабочий поток для подключения сокета к серверу, а затем ожидает до истечения времени ожидания завершения потока. Если тайм-аут истекает до завершения потока, Indy закрывает сокет и завершает поток.

Предполагается, что вы используете довольно актуальную версию Indy 10 (по крайней мере, SVN ревизии 5382 от 14 декабря 2016 г. или позднее) и не используете TIdAntiFreeze, тогда только в Windows ожидание должен завершиться немедленно после завершения рабочего потока, так как Indy делает один вызов WaitForSingleObject() в потоке в течение полного таймаута и завершается, как только WFSO выходит.

Если вы используете TIdAntiFreeze, или используете Indy на платформе, отличной от Windows, или используете версию Indy 10 до SVN rev 5382, ожидание вызывает IndySleep() (и, если необходимо, TIdAntiFreeze.DoProcess() ) в цикле с фиксированными интервалами (125 мс или 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;
...