Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Indy 10.6.5040 TCPServer Serverschliessen und Client Verbindungen trennen (https://www.delphipraxis.net/178187-indy-10-6-5040-tcpserver-serverschliessen-und-client-verbindungen-trennen.html)

Cyberaxx 22. Dez 2013 01:34

Indy 10.6.5040 TCPServer Serverschliessen und Client Verbindungen trennen
 
Hallo,

gibt es eine saubere Lösung Client verbindungen zu trennen wenn der TidTCPServer geschlossen wird?


Ein einfaches idTCPServer1.active := False reicht hier scheinbar nicht, denn dann friert das Programm ein. Ganz selten bekomme ich aber auch mal die Meldung "Die Verbindung wurde erfolgreich geschlossen" aber auch wirklich nur sehr selten und das Programm friert dennoch ein.
Wenn jeder Client von sich aus die Verbindung beendet und ich dann das Programm schliesse lässt es sich auch ohne Probleme beenden.

Die Suche und auch alle Demo Programme die ich gefunden habe bringen mich dabei leider auch nicht weiter. Im Form Close habe ich bereits alles bisher gefundene einmal probiert leider bisher ohne Erfolg. Beende ich das Programm ohne vorher idTCPServer1.active := False aufzurufen endet es in eine access violation. Denke mal das die Threads natürlich noch laufen werden und sie daher kommt.

Ich poste einfach mal mein FormClose
Delphi-Quellcode:
procedure TServerMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
var
  I: Integer;
begin
  if IdTCPServer.Active then
  begin
//    while IdTCPServer.Contexts.LockList.Count > 0 do
//    begin
//      TIdContext(IdTCPServer.Contexts.LockList[0]).Connection.IOHandler.CloseGracefully;
//    end;

    Memo1.Lines.Add(Format('%d connected Clients', [IdTCPServer.Contexts.LockList.Count]));
    if IdTCPServer.Contexts.LockList.Count > 0 then
    begin
      for I := 0 to IdTCPServer.Contexts.LockList.Count -1 do
      begin
        //TIdContext(IdTCPServer.Contexts.LockList[i]).Connection.IOHandler.CloseGracefully;
        //TIdContext(IdTCPServer.Contexts.LockList[i]).Connection.DisconnectNotifyPeer;
        //TIdContext(IdTCPServer.Contexts.LockList[i]).Connection.Disconnect;
        //TIdContext(IdTCPServer.Contexts.LockList[i]).Connection.IOHandler.DiscardAll;
        //TIdContext(IdTCPServer.Contexts.LockList[i]).Connection.IOHandler.CheckForDisconnect(False, True);
        //TIdContext(IdTCPServer.Contexts.LockList[i]).Connection.CheckForGracefulDisconnect(False);
      end;
    end;

    //IdTCPServer.Contexts.UnlockList;
    IdTCPServer.Active := False;
  end;
end;
Egal was ich versuche der Server trennt den Client nicht und der Client bekommt auch keine Meldung über einen disconnect.

Rufe ich TIdContext(IdTCPServer.Contexts.LockList[i]).Connection.Disconnect; auf dann wird am Server das OnDisconnect aufgerufen aber das war es dann auch schon.

Vielen Dank im vorraus wenn mir jemand bei dem Problem helfen könnte, ich selbst bin ratlos.

mjustin 23. Dez 2013 22:43

AW: Indy 10.6.5040 TCPServer Serverschliessen und Client Verbindungen trennen
 
Zitat:

Zitat von Cyberaxx (Beitrag 1240729)
Egal was ich versuche der Server trennt den Client nicht und der Client bekommt auch keine Meldung über einen disconnect.

Rufe ich TIdContext(IdTCPServer.Contexts.LockList[i]).Connection.Disconnect; auf dann wird am Server das OnDisconnect aufgerufen aber das war es dann auch schon.

Was da geschieht kann man ohne einen Blick auf die OnExecute Methode des Servers nicht sagen. Zwei häufige Probleme sind:

* eine in OnExecute aufgetretene Indy-Exceptions wird abgefangen (zum Beispiel durch ein leeres except ... end)
* die Anwendung blockiert wenn in OnExecute ein Deadlock auftritt, zum Beispiel im Zusammenhang mit dem VCL Thread

Beispiel:
Zitat:

(...) make sure that your server event handlers are not performing any synchronized operations to the main thread while the main thread is busy deactivating the server, otherwise a deadlock will occur.
(Quelle)
Übersetzt:
Zitat:

"achte darauf, dass die Event Handler des Servers keine "synchronized" Operationen im Hauptthread ausführen, während der Mainthread dabei ist, den Server zu beenden, da sonst ein Deadlock entsteht"
Die normale Methode einen TIdTCPServer zu beenden ist Active auf False zu setzen. Wenn der Server blockiert, warten die Clients bei einer Read oder Write Operation bis zum Timeout, das je nach Betriebssystem eine längere Zeit dauert. Indy TCP Clients erhalten keine explizite Benachrichtigung, wenn der Server die Verbindung trennt (das ist völlig normal bei TCP/IP).

Siehe auch: http://stackoverflow.com/questions/1...n-deactivating

Cyberaxx 24. Dez 2013 08:17

AW: Indy 10.6.5040 TCPServer Serverschliessen und Client Verbindungen trennen
 
Hallo,

ich habe nun das FormClose so übernommen und bekomme neben der Meldung der erfolgreichen Beendigung folgende:
Zitat:

---------------------------
Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt StringExchangeServer.exe ist eine Exception der Klasse EOSError mit der Meldung 'Systemfehler. Code: 1400.
Ungültiges Fensterhandle' aufgetreten.
---------------------------
Anhalten Fortsetzen Hilfe
---------------------------
im OnExecute:
Delphi-Quellcode:
procedure TStringServerForm.IdTCPServer1Execute(AContext: TIdContext);
var
  LLine: String;
  ID: Integer;
begin
  if IdTCPServer1.Active then
  begin
    //TIdNotify.NotifyMethod( ShowStartServerdMessage );
    //LLine := AContext.Connection.IOHandler.ReadLn(TIdTextEncoding.Default);
    ID := Integer(AContext.Connection.Socket);
    LLine := AContext.Connection.IOHandler.ReadLn();
    Memo1.Lines.Add(Format('[%.10d]: %s', [ID, LLine]));
    AContext.Connection.IOHandler.WriteLn('OK');
    //TIdNotify.NotifyMethod( StopStartServerdMessage );
  end;

  AContext.Connection.IOHandler.CheckForDisconnect(False, True);
  AContext.Connection.CheckForGracefulDisconnect(False);
end;
Wenn ich nun den Server außerhalb der IDE starte kommt die Fehlermeldung nicht...

sx2008 24. Dez 2013 09:04

AW: Indy 10.6.5040 TCPServer Serverschliessen und Client Verbindungen trennen
 
Die sauberste Art eine TCP-Verbindung zu trennen ist dem Partner einen Quit-Befehl zu senden.
Man findet diesen Weg sehr häufig in bekannten Internetprotokollen.
Bei FTP sendet der Client z.B. dem Server eine QUIT-Message und der Server beendet dann die TCP-Verbindung.
Dies gibt beiden Teilnehmern die Möglichkeit sauber alle Aufgaben abzuschliesen.
Natürlich sollte sowohl der Client als auch der Server eine Trennung der Verbindung (z.B. abgezogenes Ethernetkabel) ohne Endlosschleife überleben.
Allerdings kann ein Teilnehmer eine unterbrochene TCP-Verbindung nur dann entdecken, wenn er entweder selbst etwas sendet oder wenn er das FIN-Paket der Gegenstelle empfangen wurde.

Auf jeden Fall ist es empfehlenswert einen Quit-Befehl im Datenprotokoll vorzusehen.

Cyberaxx 25. Dez 2013 10:22

AW: Indy 10.6.5040 TCPServer Serverschliessen und Client Verbindungen trennen
 
Es geht ja genau um den Fall das die Verbindung aus unbekannten Gründen beendet wurde,eben durch gezogenes Kabel, Verlust der Internetverbindung, Rechner Absturz o.ä.

Ich habe bisher unter Windows immer direkt mit den Sockets gearbeitet und da konnte man vor dem Beenden des Servers aus Serversicht alle Clientverbindungen trennen,
Die Indys haben ja auch im Client ein OnDisconnect, das wurde in egal welchem Versuch Serverseitig die Verbindung zu trennen nie aufgerufen.

mjustin 25. Dez 2013 18:20

AW: Indy 10.6.5040 TCPServer Serverschliessen und Client Verbindungen trennen
 
Zitat:

Zitat von Cyberaxx (Beitrag 1240982)
Die Indys haben ja auch im Client ein OnDisconnect, das wurde in egal welchem Versuch Serverseitig die Verbindung zu trennen nie aufgerufen.

TIdTCPConnection (die Vorfahrklasse von TIdCustomTCPClient) hat eine Methode OnDisconnected (nicht OnDisconnect).

Diese wird aus der Methode TIdTCPConnection.Disconnect aufgerufen, also nachdem der Client die Verbindung zum Server geschlossen hat. Mit dem serverseitigen Schliessen hat diese Methode nichts zu tun.

Auf der Client-Seite kann nur mit einem kontinuierlichen Read auf der Connection das serverseitige Schliessen der Verbindung erkannt werden. Im Fall der Indy TIdTCPClient Klasse würde clientseitig eine Exception (EIdConnClosedGracefully oder ein EIdSocketError) ausgelöst, wenn das Read vom Socket nicht weiter möglich ist. Die Behandlung einer "Read Timeout" Exception muss bei diesem Verfahren auf der Clientseite u.U. auch vorgesehen werden, diese bedeutet aber nur, dass keine Daten empfangen wurden, und nicht, dass der Server die Verbindung getrennt hat.

mjustin 25. Dez 2013 18:45

AW: Indy 10.6.5040 TCPServer Serverschliessen und Client Verbindungen trennen
 
Zitat:

Zitat von Cyberaxx (Beitrag 1240908)
Delphi-Quellcode:
procedure TStringServerForm.IdTCPServer1Execute(AContext: TIdContext);
var
  LLine: String;
  ID: Integer;
begin
  if IdTCPServer1.Active then
  begin
    //TIdNotify.NotifyMethod( ShowStartServerdMessage );
    //LLine := AContext.Connection.IOHandler.ReadLn(TIdTextEncoding.Default);
    ID := Integer(AContext.Connection.Socket);
    LLine := AContext.Connection.IOHandler.ReadLn();
    Memo1.Lines.Add(Format('[%.10d]: %s', [ID, LLine]));
    AContext.Connection.IOHandler.WriteLn('OK');
    //TIdNotify.NotifyMethod( StopStartServerdMessage );
  end;

  AContext.Connection.IOHandler.CheckForDisconnect(False, True);
  AContext.Connection.CheckForGracefulDisconnect(False);
end;

Die Execute Methode wird innerhalb eines Serverthreads ausgeführt, daher sind direkte Zugriffe auf VCL Komponenten nicht sauber, da diese nicht threadsicher sind. Anstatt direkt auf Memo1 zuzugreifen kann zum Beispiel mit TThread.Queue das Logging in Memo1 threadsicher erfolgen.

Das "if IdTCPServer1.Active" then ist nicht erforderlich, da der Server den Eventhandler nicht mehr aufrufen wird wenn Active := False ist.

Cyberaxx 26. Dez 2013 01:30

AW: Indy 10.6.5040 TCPServer Serverschliessen und Client Verbindungen trennen
 
Das ist alles schon klar. Es handelt sich hier ja nur um eine Testanwendung und es ist auch nur ein Client verbunden.

Ich wollte halt alles mögliche ausprobieren was ich beim Suchen gefunden habe. Dennoch wird damit das eigentliche Problem ja nicht gelöst.
Das letzte war einfach nur im OnClose der Form Active auf False zu setzen und das Programm hing.

Derzeit ist es so das die Fehlermeldungen nur innerhalb der IDE auftauchen. Starte ich das Programm ohne IDE läuft es wunderbar.
Später ist es in einer Konsolenanwendung auf Linux und da wird auch nichts angezeigt sondern nur intern ausgewertet. Wie gesagt ich hatte so etwas schon mit den Sockets programmiert. Stammt alles noch vom Bin-Protokoll von Hagen glaub ich wars.

Aviator 9. Feb 2014 23:26

AW: Indy 10.6.5040 TCPServer Serverschliessen und Client Verbindungen trennen
 
Gibt es hier mittlerweile eine funktionierende Lösung für Indy 10? Ich stehe nämlich genau vor dem gleichen Problem. Meine OnExecute Methode des Servers ist noch vollkommen leer, da ich erst im Aufbau des Servers bin. Sobald alle Clients von selbst disconnecten, wird auch die Anwendung sauber geschlossen.

Wenn ich die Möglichkeit habe, jedem Client vorher eine Quit Befehl zu senden, wie sx2008 es auch bereits erwähnt hat, dann werde ich das natürlich machen. Mir geht es allerdings vorangig um den Fall, dass ich den Server ohne den Quit Befehl beenden muss (warum auch immer).

Wäre für eine funktionierende Lösung sehr dankbar.

CocoPops 3. Apr 2014 05:02

AW: Indy 10.6.5040 TCPServer Serverschliessen und Client Verbindungen trennen
 
Gemeinde.

Und ich habe ebenfalls genau dasselbe Problem oder fast.

Mein Client verbindet sich zum Server und der Server wird aus irgendwelchen Gründen unsauber beendet.
Die Verbindungen zu den verbundenen Clients wird also nicht korrekt beendet.

Selbst mit ReadTimeout hängt sich das Programm auf und befindet sich in einer Endlosschleife OBWOHL eine AV geworfen werden müsste:
Delphi-Quellcode:
 while not Terminated do
  begin
   Sleep(5000);

   IdTCPClient.ReadTimeout := 2000;
   IdTCPClient.IOHandler.ReadTimeout := 2000;

   if IdTCPClient.Connected then
    begin
     try
      s := IdTCPClient.IOHandler.ReadLn;
     except
      on E: Exception do
       WriteLn(PChar(E.ToString));
     
      break;
     end;
    end
   else
    begin
     break;
    end;
  end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:27 Uhr.
Seite 1 von 2  1 2      

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