Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi TIdTCPServer: OnExecute-Schleife trotz Disconnect (https://www.delphipraxis.net/139748-tidtcpserver-onexecute-schleife-trotz-disconnect.html)

Windwalker 4. Sep 2009 11:04


TIdTCPServer: OnExecute-Schleife trotz Disconnect
 
Hallo,

ich teste gerade einen von mir implementierten Windows-Dienst über Telnet.
Über Telnet simuliere ich einen Client und schicke dem IdTCPServer Kommandos.
Die werden auch alle gut erkannt.

Jedoch habe ich ein Problem mit dem Disconnect.
Wenn ich vom Client das Disconnect-Kommando sende, erkennt dieses mein Dienst auch und ruft die richtige Prozedur auf, um den Disconnect durchzuführen.
Hier führe ich u.a. folgende Anweisungen aus:
Delphi-Quellcode:
log( 'Beende Verbindunge zu Host=' + client.host+ ' Port=' + IntToStr(client.Port)+' ....',1);
// dem client mitteilen das jetzt die verbindung beendet wird
senden(client,disconnect_zeichen);
client.Context.Connection.Disconnect(True);

// Den Client aus der Liste verbundener Clients löschen
Tclient(client_list.Objects[i]).free;
client_list.Delete(i);

log('disconnect: Verbindung beendet',1);
Mein Telnet-Fenster schließt sich auch korrekt, jedoch wird beim IdTCPServer weiterhin in einer Endlosschleife das OnExecute-Event ausgelöst, was meinen Dienst zu ständigen Fehlermeldungen bringt, da er den Client nicht mehr in seiner Client-Liste führt.

Das eigentliche OnDisconnect-Event wird erst dann ausgelöst, wenn ich den Dienst wieder beende.

Wie kann ich das ständige Triggern des OnExecute-Ereignisses verhindern und ein korrektes Auslösen des OnDisconnect-Events herbeiführen?

Danke für die Hilfe!

Sascha.

Klaus01 4. Sep 2009 11:58

Re: TIdTCPServer: OnExecute-Schleife trotz Disconnect
 
.. so ganz verstehe ich das nicht.

onExecute und Endlosschleife.

Sollte onExecute ncht ungefähr so gestrickt sein:

(Beispiel aus der Hilfe)
Delphi-Quellcode:
TMyForm.MyServerExecute(AContext: TIdContext);
  var
    lCmd: string;
  begin
    lCmd := Trim(AContext.Connection.IOHandler.ReadLn);
    if AnsiSameText(lCmd, 'HELP') then
    begin
        AContext.Connection.IOHandler.WriteLn('HELP');
        AContext.Connection.IOHandler.WriteLn('QUIT');
        AContext.Connection.IOHandler.WriteLn('GETTIMESTAMP');
        AContext.Connection.IOHandler.WriteLn('');
    end

    else if AnsiSameText(lCmd, 'QUIT') then
    begin
        AContext.Connection.IOHandler.WriteLn('Goodbye...');
        AContext.Connection.IOHandler.WriteLn('');
        AContext.Connection.Disconnect;
    end

    else if AnsiSameText(lCmd, 'GETTIMESTAMP') then
    begin
        AContext.Connection.IOHandler.WriteLn(
          FormatDateTime(Now, 'yyyy-mm-ddThh:nn:ss.zzz'));
        AContext.Connection.IOHandler.WriteLn('');
    end;
  end;
Grüße
Klaus

chaosben 4. Sep 2009 12:08

Re: TIdTCPServer: OnExecute-Schleife trotz Disconnect
 
Also meine Excutes sehen sinngemäß meist so aus:
Delphi-Quellcode:
repeat
  If AThread.Connection.InputBuffer.Size > 0 then
  begin
    //...
  end;

  AThread.Connection.CheckForDisconnect(False);
  AThread.Connection.CheckForGracefulDisconnect(False);

  SleepEx(1, true);
until (not AThread.Connection.Connected);
Ich denke mal du hast die beiden Checks vergessen.

Windwalker 4. Sep 2009 12:44

Re: TIdTCPServer: OnExecute-Schleife trotz Disconnect
 
Danke schonmal!

Aber leider bringt's das bei mir nicht.
Zum einen habe ich keinen "AThread"-Parameter, sondern "AContext" und dieser besitzt in seiner Connection kein CheckForDisconnect, sondern nur das CheckForGracefulDisconnect (ich nutze Delphi 2006).

Aber auch, wenn ich nur das CheckForGracefulDisconnect und das Sleep einbaue, wird das OnExecute-Event immer wieder erneut ausgelöst.
Ich habe bei meiner Recherche dazu auch folgendes gefunden:
Zitat:

Jede Verbindung, die ein TIdTCPServer entgegen nimmt, startet einen eigenen Thread. Und dieser Thread hat eben eine OnExecute Prozedur. Diese wird so lange wiederholt aufgerufen, wie die Verbindung besteht.

Und weil sich dein Programm aufhängt:
Ich rate mal, bau ein

Delphi-Quellcode:
Sleep(1);
am Ende der OnExecute Prozedur ein.
Also scheint meine Verbindung noch zu bestehen, ob ich bei der Verarbeitung meines Disconnect-Kommandos ein
Delphi-Quellcode:
client.Context.Connection.Disconnect(True);
mache.

Über weitere Hilfe freue ich mich sehr!

Danke,
Sascha.

Klaus01 4. Sep 2009 12:52

Re: TIdTCPServer: OnExecute-Schleife trotz Disconnect
 
Hallo,

kanst Du Deine OnExecute und OnDisconnet Methoden
hier zeigen?

Grüße
Klaus

Windwalker 4. Sep 2009 12:59

Re: TIdTCPServer: OnExecute-Schleife trotz Disconnect
 
Klar!

OnExecute:
Delphi-Quellcode:
procedure TmyService.IdTCPServerExecute(AContext: TIdContext);
var
  cli :TClient;
  s, s1 : string;
  i, laenge :integer;
  fehler : boolean;
  ThreadKey : string;
begin
  // Hier reagiert der Server auf eingehende Nachrichten
  try
    fehler := FALSE;
    ThreadKey := get_key(AContext);
    i:=client_list.IndexOf(ThreadKey);
    if i >= 0 then
    begin
      cli := TClient(client_list.objects[i]);
      log('Anfrage von Client '+cli.host,0);
      while (not fehler) and (AContext.Connection.Connected) do
      begin
        try
          s1 := AContext.Connection.IOHandler.ReadString(7);
          laenge := StrToIntDef(s1, -1);
          log('Länge des Pakets: '+s1,0);
          if laenge>0 then
          begin
            s1 := AContext.Connection.IOHandler.ReadString(laenge);
            cli.anfrage := s1;
            Extrahiere_anfrage(cli.anfrage,s);
            if s<>'' then
            begin
              log('Anfrage: '+s,0);
              bearbeite_anfrage(s,cli);
            end;
          end
          else
          begin
            log('Längenangabe des zu empfangenden Buffers ist fehlerhaft ('+s1+')',1);
            fehler := TRUE;
          end;
        except
          on e :EIdConnClosedGracefully do
          begin
            log('ConnClosedGracefully',1);
            fehler := TRUE;
          end;
          on e :Exception do
          begin
            log('Fehler beim Lesen von Host='+cli.host+
                          ' Port='+IntToStr(cli.port),1);
            log(e.Message,9);
            log(e.ClassName,9);
            fehler := TRUE;
            disconnect(cli);
          end;
        end;
      end; // While
    end
    else
      log('Client nicht in Liste',1);
  except on e:exception do
    log('Feher in der Executeroutine des Servers: '+e.Message,9);
  end;
end;
Das OnDisconnect:
Delphi-Quellcode:
procedure TmyService.IdTCPServerDisconnect(AContext: TIdContext);
var
  Cli:TClient;
  i:integer;
  s:string;
begin
  // hier wird ein Client abgemeldet
  log('OnDisconnect: Client-List Size: '+IntToStr(client_list.Count),2);
  if AContext <> nil then begin
    Cli:= nil;
    s:= get_key(AContext);
    log('OnDisconnect des Clients: '+s,1);
    ThreadLock_ClientListe.Enter;
    try
       i:= client_list.IndexOf(s);
       log('OnDisconnect: Client-Index='+IntToStr(i),2);
       if (i > -1) then begin
          log('OnDisconnect: Index > -1 --> Disconnect aufrufen'+s,1);
          Cli:= TClient(client_list.objects[i]);
       end;
    finally
       ThreadLock_ClientListe.leave;
    end;
    if Cli <> nil then begin
       disconnect(Cli);
    end;
  end;
end;

Und zwei Methoden, die im OnExecute benutzt werden:

bearbeite_anfrage:
Delphi-Quellcode:
procedure TmyService.bearbeite_anfrage(var anfrage: String;
  cli: TClient);
var
  bearbeitet : boolean;
  msg : string;
begin
  // hier werden die von Clients eintreffenden Nachrichten bearbeitet
  log('Bearbeite Anfrage: '+cli.anfrage,2);
  bearbeitet := False;
 
  // hier die Nachrichten auswerten, z.B.
  if anfrage = schreibe_in_log then
  begin
    lies_nachricht(cli.anfrage, msg);
    log('WRITETOLOG: '+msg, 9);
    bearbeitet := True;
  end
  else if anfrage = disconnect_client then
  begin
    // disconnect des clients kommt gleich --> aus liste rausschmeißen
    disconnect(cli);
  end
  else
  begin
    log('Befehl '''+anfrage+''' nicht verstanden.',9);
  end;
  if bearbeitet then
    senden(cli,anfrage);
  anfrage:= '';
end;
Das Disconnect, welches aus bearbeite_anfrage, beim Eintreffen eines Disconnect-Kommandos aufgerufen wird:
Delphi-Quellcode:
procedure TmyServiceService.disconnect(cli: Tclient);
var
  i:integer;
begin
  (* Client wird aus der Liste entfernt und ein Disconnect durchgeführt *)
  try
    try
      ThreadLock_ClientListe.Enter;
      if client_list.Find(get_key(cli.Context),i) then
      begin
        log( 'Beende Verbindunge zu Host=' + cli.host+ ' Port=' + IntToStr(cli.Port)+' ....',1);
        // dem client mitteilen das jetzt die verbindung beendet wird
        senden(cli,disconnect_zeichen);
        cli.Context.Connection.DisconnectNotifyPeer;
        Tclient(client_list.Objects[i]).free;
        client_list.Delete(i);
        log('disconnect: Verbindung beendet',1);
      end;
    finally
      ThreadLock_ClientListe.Leave;
    end;
  except on e:exception do
       log('Fehler beim disconnect: '+e.Message,9);
  end;
end;

Klaus01 4. Sep 2009 19:15

Re: TIdTCPServer: OnExecute-Schleife trotz Disconnect
 
Guten Abend,

ich habe mal ein paar kleine Test mit einem simplen Server und Client gemacht.

Pro verbunden Client wird ein onExecute Thread eröffnet (sichtbar im Taskmanager),
wenn ich nun in der onExecute folgende Schleife habe:

Delphi-Quellcode:
procedure TForm1.myOnExecute(aContext: TIdContext);
begin
  while aContext.Connection.Connected do
    sleep(10);
end;
Wird nun die Verbindung vom Client beendet, beendet sich auch die onExecute Methode.
Der Thread wird beendet und gut ist.

Ein onDisconnect Methode habe ich nicht benutzt.

Was bei Dir schief läuft - keine Ahnung.

Grüße
Klaus

quantum 5. Sep 2009 12:58

Re: TIdTCPServer: OnExecute-Schleife trotz Disconnect
 
Ich bin mir ziemlich sicher, es liegt an den verschluckten Exceptions.

Entferne mal das äußere "try..except..end" Konstrukt im OnExecute-Handler oder füge wenigestens "raise" als letzten Befehl unter "except" ein.

Wenn ich mich recht erinnere, beendet der Indy-Server den Thread nur wenn er mit einer Exception konfrontiert wird.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:37 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz