Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Indy TCP-Anwendung: Wie eine tote Verbindung loswerden? (https://www.delphipraxis.net/92699-indy-tcp-anwendung-wie-eine-tote-verbindung-loswerden.html)

FoxOne 24. Mai 2007 15:59


Indy TCP-Anwendung: Wie eine tote Verbindung loswerden?
 
Hallo!

Da ich mit fehlender Kenntnis übergreifender Zusammenhänge der Indy-TCP Funktionalität zu kämpfen habe, bitte ich in folgener Sache um Hilfe:

Mit meiner Indy9 TCPServer/Client-Anwendung habe ich ein Problem mit Client-seitig unterbrochenen Verbindungen. Wenn ein Client sich für bestimmte Zeit nicht rührt bzw. keine Daten erhält, kann ich davon ausgehen, dass die Verbingung unterbrochen wurde (z.B. WLAN-Ausfall). In diesem Fall hängt der neuerliche Verbindungsaufbau zum Server. Der Server muss neu gestartet werden, dass die Anwendung wieder läuft. :pale:
Das passiert bei langsamer Verbindung (GPRS) immer, bei schneller (XDSL) selten.

Mein Ansatz war, den zugehörigen idPeerThread von 'Hand' zu terminieren, weil ich diesem im Verdacht habe, in einem Deadlock zu hängen. Leider nutzt das nichts (Code siehe unten).

Hat jemand eine Idee, wie ich den Server wieder frei bekomme bzw. die 'Altlasten' der toten Verbindung loswerte?

Vielen Dank!




Delphi-Quellcode:

//--- dip : IP-Adresse der Verbindung, deren Thread terminiert werden soll


function TBaseTCPServer.TerminateConnection(dip: string): boolean;
var i : integer;
    List : TList;
begin

  result:=true;
  if not idTCPServer.Active then Exit;

  List := idTCPServer.Threads.LockList;
 

  i:=List.count-1;
  while (i>=0) and
      (AnsiCompareStr(TidPeerThread(List.Items[i]).Connection.Socket.Binding.PeerIP,dip)<>0) do
    dec(i);

  if i>=0 then
  begin
    try
      //--- Sicherheitshalber disconnecte ich alles, was mit dem Thread in Zusammmenhang steht
      TidPeerThread(List.Items[i]).Connection.Disconnect;
      TidPeerThread(List.Items[i]).Connection.DisconnectSocket;
    finally
        TidPeerThread;(List.Items[i]).Terminate;
    end;
  end;

  idTCPServer.Threads.UnlockList;
  list.Clear;
 
end;

supermuckl 24. Mai 2007 16:50

Re: Indy TCP-Anwendung: Wie eine tote Verbindung loswerden?
 
Liste der Anhänge anzeigen (Anzahl: 1)
ähm im server kannst du doch mit timeouts arbeiten ?!
dann gibts auch keine deadlocks sondern nur kleine verzögerungen durch die ablaufenden timeouts

es geht ja sowieso alles für jeden client in einem eigenen thread ab (OnExecute)


hab da schon einige client serveranwendungen gebaut und nie probs gehabt

beispielsweise im OnServerExecute kannst du die ReadLn's mit Timeouts versehen:

athread.Connection.ReadLn(#$A,1000); <- Timeout von 1sec

du kannst auch hergehen und die clients dauerhaft pakete an den server schicken lassen
und wenn die nimmer ankommen kann der server die automatisch alle killen (die client thread leichen)

hab dir mal nen altes projekt mit indy9 angehängt, das diese pingerei usw verdeutlichen soll
allerdings ohne killen :)

FoxOne 24. Mai 2007 17:37

Re: Indy TCP-Anwendung: Wie eine tote Verbindung loswerden?
 
Vielen Dank für die Antwort und die Files!

Nun ist es so, dass ich eigentlich eh dasselbe bei meinem Projekt mache:
Der Client fragt in regelmäßigen Zeitabständen um Daten an, wenn diese Zeit überschritten wird, weiß der Server, dass der Client verblichen ist. Dann wir die oben gepostete Routine 'TerminateConnection' aufgerufen, die den Server aber leider im Deadlock (?) verbleiben läßt.
Den Deadlock kann ich leider nicht so einfach durch ein Zeitlimit ausschließen, weil ich (unter anderem) ReadStream verwende, das anders als Readln keinen Parameter dafür hat.

Ich habe übrigens auch nur Disconnect und DisconnectSocket probiert, das führt aber zum selben Effekt: Man kann sich nicht mehr mit dem Server verbinden. Im Detail: es wird ein neuer Thread erstellt, aber in die OnReceive-Prozedur gelangt man nicht. Auch gar nichts tun bringt nichts, dann wird laufend die Exception 'Socket Error # 10054: connection reset by peer' in OnReceive geworfen (obwohl der Client gar nichts mehr über die unterbrochene Verbindung schicken kann??).


Kurz nach Beenden der Server-Anwendung kommt übrigens die Meldung 'Terminate Thread Timeout'.

Den ganzen Tag hab ich mir schon damit um die Ohren geschlagen, ich bin wirklich für jeden Hinweis dankbar!

Viele Grüße,
Martin

supermuckl 24. Mai 2007 19:07

Re: Indy TCP-Anwendung: Wie eine tote Verbindung loswerden?
 
ich vermute mal, das dein deadlock, der im clientthread hängt, auch den thread.terminate abhält zu wirken.
somit sind auch dann die threads noch alle da und können nicht beendet werden, wenn dein prog abgeschossen wird.

eine lösung für blockierte threads abschiessen hab ich aber net (evtl von aussen irgendwie killen mit ner api über die threadid? ka)

Udontknow 25. Mai 2007 12:40

Re: Indy TCP-Anwendung: Wie eine tote Verbindung loswerden?
 
Hallo!

Ich würde die Threadhandhabung der Serverkomponente überlassen.
Sollte es nicht reichen, innerhalb der OnExecute-Routine einfach das Disconnect aufzurufen? Der Verbindungsthread wird doch dann von alleine beendigt. Vielleicht noch ein Abort dazu, damit du die (Sub-)Routinen garantiert alle verlässt, und fertig...

Cu,
Udontknow

FoxOne 25. Mai 2007 15:52

Re: Indy TCP-Anwendung: Wie eine tote Verbindung loswerden?
 
Vielen Dank für die Antwort!

Habe inzwischen weiter herumprobiert und kann bestätigen, dass der Socket den Thread ordentlich wegputzt, wenn eine bestimmte Zeit verstrichten ist (irgendein Timeout von ca. 30s, ich weiß nicht, wie bzw. wo der zu setzen ist). Es wird dann die bereits erwähnte Exception 'Connection reset by peer' geworfen.
Bei meinen Internetrecherchen bin ich auf den Tip gestoßen, diese Exception nocheinmal anzustoßen, weil sie Indy intern noch verwertet, also:
Delphi-Quellcode:

uses ....,IdException;

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
begin

  try

  //--- Server Arbeit hier
  //..
  //..

  except
    on e: EIdSocketError do
    begin //-- Indy exception
        ErrorOut('Socket Exception in OnExecute: ' + e.message);
        Raise; //--- wichtig!
    end;

    on e: exception do
    begin //-- andere exception
        ErrorOut('Exception in MyOnReceive: ' + e.message);
        Raise; //--- wichtig?

    end;
  end;

end;

Dies hat soweit geholfen, dass die Exception nun nur mehr einmal kommt. Wenn ich mir vor dem Timeout und danach die Threadliste anschaue, dann ist nachher der 'tote' Thread tatsächlich weg.
Beim erneuten starten des Clients wird dann die Verbindung soweit aufgebaut, dass ein neuer TidPeerThread in der Threadliste ist, aber leider wird nun die OnExecute-Prozedur des Servers nicht mehr aufgerufen. Somit hängt Client-Server-Verbindung, aber eben nur in etwa 50% der Fälle bei langsamer Verbindung. Der Rest der Server-Applikation (Datenerfassung etc.) läuft ungestört weiter. Beim Beenden des Servers kommt dann wenig überraschen die Exception 'Terminate Thread Timeout'.

Habe inzwischen auch einen OnListenException-Handler implementiert, der springt aber nicht an.

Ich bin also weiterhin ratlos. Wie kann ich den Server wieder freibekommen?


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:49 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