Как остановить событие TCPServer OnExecute от бесконечного выполнения после AContext.Connection.Disconnect?

У меня есть это TCPServerExecute событие, выполнение которого я хочу остановить после того, как вручную отключу соединение с клиентом:

procedure TMainForm.TCPServerExecute(AContext: TIdContext);
var
  TCPClient : TIdTCPClient;
begin
try
  TCPClient := nil;
  try
    TCPClient := TIdTCPClient.Create(nil);
    if aConditionIsMet then begin 
      AContext.Connection.IOHandler.WriteLn('Disconnected from server.');
      AContext.Connection.Disconnect;
      Exit;
    end;
  finally
    FreeAndNil(TCPClient);
  end;
except on e : Exception do
  begin
    MainForm.Log('error in Execute=' + e.Message);
  end;
end;
end;

и на стороне клиента все в порядке, но на стороне сервера я бесконечно перебираю TCPServerExecute. Что я делаю не так и как я могу остановить выполнение TCPServerExecute после ввода AContext.Connection.Disconnect?


person Viktor Anastasov    schedule 19.05.2014    source источник
comment
почему код создает TIdTCPClient, а затем вообще не использует его? (обработчик события OnExecute выполняется в цикле, поэтому это вызовет высокую нагрузку)   -  person mjn    schedule 19.05.2014
comment
@mjn Он не используется, потому что я не передал весь код процедуры - только небольшую его часть, где проблема все еще возникает.   -  person Viktor Anastasov    schedule 19.05.2014
comment
Вместо использования глобального логического флага для отключения клиентов вы можете установить TCPServer.Active := False, и сервер отключит всех подключенных клиентов.   -  person mjn    schedule 19.05.2014
comment
@mjn И если я хочу отключить только текущего клиента, AContext.Connection.Disconnect не способ это сделать?   -  person Viktor Anastasov    schedule 19.05.2014
comment
Да, но aConditionIsMet каким-то образом необходимо знать, принадлежит ли текущий AContext тому соединению, которое следует отключить. Так что это будет функция типа ConditionIsMet(AContext: TIdContext)   -  person mjn    schedule 19.05.2014


Ответы (1)


Цикл продолжается, потому что исключения Indy обрабатываются некорректно.

Либо удалите обработчик исключения, либо повторно вызовите исключение после регистрации:

except 
  on e : Exception do
  begin
    MainForm.Log('error in Execute=' + e.Message);
    raise;
  end;
end;

p.s. доступ к MainForm из потока сервера не является потокобезопасным. Есть много решений для улучшения этого кода (TThread.Queue - одно из них).

person mjn    schedule 19.05.2014
comment
Хотя вы правы насчет всех исключений, что-то мне подсказывает, что это будет сложнее, чем простое повторное повышение, потому что я считаю, что вы не должны позволять клиентскому исключению пузыриться на сервере. - person TLama; 19.05.2014
comment
@mjn - Удаление обработчика исключений сработало, но я не знаю, что вы имеете в виду, когда повторно вызываете исключение после регистрации? - person Viktor Anastasov; 19.05.2014
comment
@TLama Indy TIdTCPServer предназначен для использования исключений таким образом: пусть они всплывают, сервер позаботится о них. И некоторые типы исключений, специфичные для Indy, вызовут отключение клиента. - person mjn; 19.05.2014
comment
@mjn - Большое спасибо - оба ваших решения отлично сработали :) - person Viktor Anastasov; 19.05.2014
comment
@mjn, это зависит от приложения. Что, если он не хочет разрывать соединение с сервером при выходе из строя внутреннего клиента? Что, если вы хотите любезно сообщить клиенту сервера, что внутренний клиент отказал? - person TLama; 19.05.2014
comment
@TLama теперь я понимаю, что вы говорите о TCPClient, динамически создаваемом в обработчике событий OnExecute - да, это другая часть этажа, которой нужна собственная обработка исключений и бизнес-логика, чтобы решить, пузыриться или нет - person mjn; 19.05.2014