4

I have this TCPServerExecute event that I want to stop from executing after I manually disconnect the connection with the client :

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;

and at client side everything's fine but on server-side I loop through TCPServerExecute infinitely. What am I doing wrong and how can I stop TCPServerExecute from executing after I type AContext.Connection.Disconnect ?

Viktor Anastasov
  • 1,093
  • 3
  • 17
  • 33
  • why does the code create a TIdTCPClient and then not use it at all? (the OnExecute event handler is executed in a loop, so this will cause high load) – mjn May 19 '14 at 14:28
  • @mjn It's not used because I haven't passed the entire code of the procedure - only a small part from it where the problem still occurs. – Viktor Anastasov May 19 '14 at 14:37
  • Instead of using a global boolean flag to disconnect the clients, you can set `TCPServer.Active := False` and the server will disconnect all connected clients – mjn May 19 '14 at 14:48
  • @mjn And if I want to disconnect only the current client isn't `AContext.Connection.Disconnect` the way to do that ? – Viktor Anastasov May 19 '14 at 14:49
  • Yes, but `aConditionIsMet` somehow needs to know if the current AContext belongs to the connection which should be disconnected. So it would be a function like `ConditionIsMet(AContext: TIdContext)` – mjn May 19 '14 at 16:38

1 Answers1

2

The loop continues because Indy exceptions are not handled correctly.

Either remove the exception handler, or re-raise the exception after logging:

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

p.s. accessing the MainForm from the server thread is not thread-safe. There are many solutions to do improve this code (TThread.Queue is one of them).

mjn
  • 36,362
  • 28
  • 176
  • 378
  • Although you are right about eating all exceptions, something tells me this will be more difficult than a simple re-raise, because I think that you shouldn't let a client's exception bubble to the server. – TLama May 19 '14 at 14:38
  • @mjn - Removing the exception handler worked but I don't know what you mean by "re-raise the exception after logging" ? – Viktor Anastasov May 19 '14 at 14:41
  • @TLama Indy TIdTCPServer is designed to use exceptions this way: let them bubble, the server will take care of them. And some Indy specific exception types will cause a client disconnect. – mjn May 19 '14 at 14:44
  • @mjn - Thanks a lot - both of your solutions worked nicely :) – Viktor Anastasov May 19 '14 at 14:47
  • @mjn, that depends on application. What if it's not wanted to *kill the server connection* when the inner client fails ? What if you would like to tell to the server's client kindly that the inner client failed ? – TLama May 19 '14 at 14:52
  • @TLama now I understand you are talking about the TCPClient created dynamically in the OnExecute event handler - yes this is a different part of the storey, which needs its own exception handling and business logic to decide wether to bubble or not – mjn May 19 '14 at 14:55