Еще одна тупиковая ситуация с TIdTCPServer - PullRequest
1 голос
/ 05 августа 2011

У меня возникла известная проблема: IdTCPServer зависает при попытке его деактивировать. Я прочитал много статей, касающихся этой проблемы, и я знаю, что происходит тупик. Но мне трудно понять, какой именно код вызывает проблему. У моего приложения нет даже окна, поэтому синхронизированный вызов компонента VCL не является причиной. Получил 2 обработчика событий OnConnect и OnExecute. Проблема появляется в OnConnect. Код:

TMyServer = class
...
private
FServer: TIdTCPServer;
...
end;

TMyServer.ServerConnect(AContext...);  //onconnect
begin
  try
    if not Condition then
    begin
      ...
      AContext.Connection.Disconnect;
      Stop;
    end
    else Start;
  except
    on E: Exception do
      if E is EIdException then raise;
      ...  
  end;

TMyServer.Start;
begin
  ...
  FServer.active := true;
  FServer.StartListening;
  ...
end;

TMyServer.Stop;
begin
  ...
  FServer.StopListening;
  FServer.Active := false;
  ...
end;

TMyServer.ServerExecute(AContext...);  //onexecute
begin
  LLine := AContext.Connection.IOHandler.ReadLn;
  case (LLine) of
  ...
  end;
end;

Код ServerExecute довольно большой, поэтому я не буду размещать его здесь. Кроме того, я полагаю, что проблема не в этом.
Когда клиент подключается к серверу и условие ложно, сервер пытается отключить этого клиента и зависает на линии FServer.Active := false;
Я уже исключил всю регистрацию из кода (я думал, что проблема была в доступе потоков к файлу журнала). Таким образом, в обработчиках событий есть только вычисления и ничего, что может вызвать тупик). Я читал о повторном повышении исключений в Indy, но это мне тоже не помогло.
Я был бы признателен за любую помощь и объяснения, потому что я думаю, что я не полностью понимаю цель этой ситуации.

1 Ответ

2 голосов
/ 06 августа 2011

Вы пытаетесь деактивировать сервер из одного из его обработчиков событий.Вот почему ваш код блокируется.

Установка для свойства Active значения False завершает все запущенные клиентские потоки и ожидает их полного завершения.События OnConnect, OnDisconnect и OnExecute запускаются в контексте этих потоков.Таким образом, в результате получается поток, который пытается подождать сам и блокирует блокировку.

Для безопасной деактивации сервера вы должны сделать это асинхронно, например, используя класс TIdNotify, например:

uses
  ..., IdSync;

TMyServer.ServerConnect(AContext...);  //onconnect
begin
  try
    if not Condition then
    begin
      ...
      AContext.Connection.Disconnect;
      TIdNotify.NotifyMethod(Stop);
    end
    else
      TIdNotify.NotifyMethod(Start);
  except
    on E: Exception do
      if E is EIdException then raise;
      ...  
  end;
end;

Кстати, вам не нужно вызывать методы StartListening() и StopListening() вручную.Установщик свойств Active делает это внутренне для вас.

...