Einzelnen Beitrag anzeigen

Benutzerbild von DataCool
DataCool

Registriert seit: 10. Feb 2003
Ort: Lingen
909 Beiträge
 
Delphi 10.3 Rio
 
#27

AW: Indy TCPServer beenden mit toten Clients

  Alt 13. Jun 2012, 11:21
Hi Hsg,

ich habe mir das ganze gerade mal auf die schnelle angeschaut
und ein paar kleine Änderungen gemacht ohne Testmöglichkeit, deshalb ohne Gewähr auf Richtig.

Erstmal habe ich innerhalb der Procedure DisConnectAllClients
das Senden mit Try except gekapselt, damit auch alle Clients wirklich die Nachricht bekommen
und nicht wenn bei z.b. Client2 eine Exception während des Senden auftritt und die restlichen Clients dann
nicht mehr benachrichtigt werden.
Des Weiteren habe ich LockList und UnlockList mit Try finally abgesichert !! Unbedingt zu empfehlen !!!
Delphi-Quellcode:
procedure TPDAServer.DisConnectAllClients();
var i : Integer;
    oCont : TIdContext;
    oList : TList;

begin
  // Benachrichtige die Clients vom Ableben:
  // ACHTUNG: Innerhalb der IDE tritt irgendwo eine EIdClosedSocked Exception auf
  // das ist wohl ein bekanntes Problem, ist in der EXE wohl nicht der Fall!
  if oServer <> nil then begin
    oList := oServer.Contexts.LockList;
    try
      if oList.Count > 0 then begin
        for i:=0 to oList.Count -1 do begin
          oCont := TIdContext(oList[i]);
          if (oCont <> nil) and (oCont.Connection <> nil) then begin
            try
              // nach dem Aufruf von CheckDataOnSource kannst Du Dich auch auf die Property connected verlassen
              oCont.Connection.AContext.Connection.IOHandler.CheckForDataOnSource(100);
              if oCont.Connection.Connected then begin
                Info('Aufforderung zur Abmeldung für Client: ' + GetHostName(oCont));
                oCont.Connection.IOHandler.Writeln('CLOSE_REQUEST@ ');
              end;
    // oCont.Connection.Disconnect(false);
            except
              on e:exception do begin
                {$IFDEF DEBUG}
                OutputDebugString(PChar(e.Mesage));
                {$ENDIF}
              end;
            end;
          end; // if oCont <> nil
        end;
      end;
    finally
      oServer.Contexts.UnlockList();
    end;
  end; // if oServer <> null
end;
Weitere Änderung im OnExecute:
Delphi-Quellcode:
// .. snip
      //AContext.Connection.IOHandler.CheckForDisconnect(False, True);
      //AContext.Connection.CheckForGracefulDisconnect(False);
      // Damit Exceptions auch ausgelöst und weitergereicht werden, geändert zu:
      AContext.Connection.IOHandler.CheckForDisconnect(true, True);
      AContext.Connection.CheckForGracefulDisconnect(true);

      // Meiner Meinung nach sogar beides überflüssig an dieser Stelle
      // Wenn's hier benutzt wird, dann mit Exception

// .. snip
Ein weiterer sehr entschiedener Punkt ist:
Wo wird DisConnectAllClients aufgerufen, in Deinem Sourcecode Auszug ist kein Aufruf davon zu finden!
Und gerade im Setter der Active Eigenschaft Deines Servers, solltest Du vorm setzen der Eigenschaft
Active = false, woher alle Clients benachrichtigen, das der Server heruntergefahen wird und die Clients
die Verbindung trennen sollen.

Delphi-Quellcode:
procedure TPDAServer.SetActive(const lAct: Boolean);
var
    dtTimeout : TDateTime;
    iClientCount : Integer;

    // etwas unschön hier reingequescht, kann/darf gerne woanders positioniert werden
    function getClientCount : Integer;
    var oList : TList;
    begin
      oList := oServer.Contexts.LockList;
      try
        result := oList.Count;
      finally
        oServer.Contexts.UnlockList();
      end;
    end;

begin

  if not lAct then begin
    // Server soll beendet werden, vorher alle noch aktiven Clients benachrichtigen
    DisConnectAllClients;

    // maximalen Timeout berechnen, der auf Clients gewartet wird
    dtTimeOut := now + 10/24/60/60; // jetzt + 10 Sekunden

    iClientCount := getClientCount;
    while (iClientCount > 0) and (now < dtTimeout) do begin
      // Application.Processmessages; // eventuell bei Bedarf
      Sleep(250); // warten ...
      iClientCount := getClientCount;
    end;

  end;
  oServer.Active := lAct;
end;
Das sollte Dein Problem jetzt aber lösen (hoff ich)

Greetz Data
Angehängte Dateien
Dateityp: pas uPDAServer.pas (15,8 KB, 14x aufgerufen)
Der Horizont vieler Menschen ist ein Kreis mit Radius Null, und das nennen sie ihren Standpunkt.
  Mit Zitat antworten Zitat