Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Indy TCPServer beenden mit toten Clients (https://www.delphipraxis.net/168753-indy-tcpserver-beenden-mit-toten-clients.html)

hsg 8. Jun 2012 11:16

Indy TCPServer beenden mit toten Clients
 
Hallo zusammen,

ich kämpfe gerade mit einem TIdTCPServer(Indy10), der sich mit unseren PDAs übers WLan unterhält. Leider kommt es vor, dass der Mitarbeiter mit seinem PDA den Bereich des WLan verlässt oder der PDA aus anderen Gründen die Verbindung zum Netzwerk verliert.

Kommt der PDA wieder ins Netz, meldet er sich beim Server auf einem neuen Port an und gut ist. Im Server ist der PDA zweimal aufgelistet, macht aber keine Probleme. Wird der Server nun beendet, bekommen beide Client-Verbindungen die Abmeldung und der Server lässt sich dann sauber beenden.

Anders ist es allerdings, wenn der PDA beim Beenden des Servers nicht mehr erreichbar ist. Dann versucht der Server die Verbindung zu beenden, bleibt dabei aber hängen.

Beim Server ist die TerminateWaitTime gesetzt,
in den jeweiligen Connections die ConnectTimeout und die ReadTimeout.

Leider bekomme ich kein Disconnect vom Client, auch keine Exception im Server.

Was kann ich tun, damit der Server sauber beendet werden kann?

Gruß
Jörg

Umgebung: Server mit Indy10.1 in BDS 2006 geschrieben.

mjustin 8. Jun 2012 12:33

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Umgebung: Server mit Indy10.1 in BDS 2006 geschrieben.
Wie versucht der Server denn die Verbindung zu beenden - soll da noch etwas über den IOHandler (Socket) an den Client gesendet werden, und ist dabei ein Timeout angegeben?

p.s. das ist eine sehr alte Version, spricht etwas dagegen die aktuelle Version 10.5.8.(4768) zu testen?

hsg 8. Jun 2012 12:53

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von mjustin (Beitrag 1170020)

Wie versucht der Server denn die Verbindung zu beenden - soll da noch etwas über den IOHandler (Socket) an den Client gesendet werden, und ist dabei ein Timeout angegeben?

Der Server versucht etwas zu senden (IOHandler.Writeln('CLOSE_REQUEST@ ');) Über diesen Punkt kommt er aber ohne Probleme hinweg.
Wie gebe ich ein TimeOut beim Schreiben an?
Hängen scheint er beim beenden des Threads.

Was mich irritiert ist die Tatsache, dass der tote Socket beim erreichen des Netzes ja erfolgreich geschlossen wird (wenn also zwei Socketverbindungen vorhanden sind und die zweite noch lebt. Nur wenn das Gerät überhaupt nicht mehr ansprechbar ist, begeht der Server Suizid.


Zitat:

Zitat von mjustin (Beitrag 1170020)
p.s. das ist eine sehr alte Version, spricht etwas dagegen die aktuelle Version 10.5.8.(4768) zu testen?

Werde ich Montag mal ausprobieren.

Gruß Jörg

mjustin 8. Jun 2012 13:05

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von hsg (Beitrag 1170029)
Der Server versucht etwas zu senden (IOHandler.Writeln('CLOSE_REQUEST@ ');) Über diesen Punkt kommt er aber ohne Probleme hinweg.

Ok, das Senden an den Socket funktioniert - das ist normal, auch wenn die Gegenseite nicht zuhört.

und das:

Zitat:

Anders ist es allerdings, wenn der PDA beim Beenden des Servers nicht mehr erreichbar ist. Dann versucht der Server die Verbindung zu beenden, bleibt dabei aber hängen.
verstehe ich als ob nach dem obigen "Writeln" noch etwas vom Server gemacht wird, zum Beispiel auf die Antwort des Clients auf das 'CLOSE_REQUEST@' zu warten. Und das kann natürlich nicht mehr erfolgreich sein wenn der Client nicht da ist. In diesem Fall sollte der Server die dann auftretende Exception (vermutlich ReadTimeOut) eventuell nicht mehr behandeln und Indy automatisch den Thread beenden lassen, wenn ich mich richtig erinnere.

Beim IOHandler.Read kann ein TimeOut direkt als Parameter angegeben werden. Der Default ist relativ lange, was wie ein Hänger aussehen kann.

(Send-Timeouts sind plattformspezifisch, nicht als Parameter oder Properties, konfigurierbar und hier anscheinend nicht das Problem)

Hope this helps,

hsg 8. Jun 2012 13:19

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von mjustin (Beitrag 1170032)
Zitat:

Anders ist es allerdings, wenn der PDA beim Beenden des Servers nicht mehr erreichbar ist. Dann versucht der Server die Verbindung zu beenden, bleibt dabei aber hängen.
klingen als ob nach dem obigen "Writeln" noch etwas vom Server gemacht wird, zum Beispiel auf die Antwort des Clients auf das 'CLOSE_REQUEST@' zu warten. Und das kann natürlich nicht mehr erfolgreich sein wenn der Client nicht da ist. In diesem Fall sollte der Server die dann auftretende Exception (vermutlich ReadTimeOut) eventuell nicht mehr behandeln und Indy automatisch den Thread beenden lassen, wenn ich mich richtig erinnere.

Der ReadTimeOut ist gesetzt (300 sek. habe aber den Server eine viertel Stunde in Ruhe gelassen, er hing immer noch :(
Die Clients schließen auf die Aufforderung nur die Sockets. Es wird nichts gesendet.
Exception wird ja auch keine geworfen. Zu keinem Zeitpunkt.

Zitat:

Zitat von mjustin (Beitrag 1170032)
Beim IOHandler.Read kann ein TimeOut direkt als Parameter angegeben werden. Der Default ist relativ lange, was wie ein Hänger aussehen kann.

(Send-Timeouts sind plattformspezifisch, nicht als Parameter oder Properties, konfigurierbar und hier anscheinend nicht das Problem)

Hope this helps,

Einzig allein in der OnExecute-Routine wird der Inputbuffer ausgelesen

Delphi-Quellcode:
  if AContext.Connection.IOHandler.InputBufferIsEmpty then
     begin
       SleepEx(10, True);
       Exit;
     end;

  oAnswer                   := TStringList.Create();

  try
    bTest                   := True;
    cMess                   := Trim(AContext.Connection.IOHandler.InputBufferAsString());
    //cMess                    := Trim(AContext.Connection.IOHandler.ReadLn());

    if cMess = '' then
       begin
         Exit;
       end;
...
es wird also explizit vorher nachgesehen, ob etwas im Puffer steht. Auch kommt er beim Beenden nicht an diese Stelle. Kann somit leider ausgeschlossen werden :(

mjustin 8. Jun 2012 14:26

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von hsg (Beitrag 1170037)

Exception wird ja auch keine geworfen. Zu keinem Zeitpunkt.

Werden Exceptions eventuell so behandelt, dass Indy sie nicht bemerkt? Indy verwendet einige (selbstdefinierte) Exceptions wie EIdConnClosedGracefully zur Steuerung:

Zitat:

You need to get rid of your exception handling, or at least re-raise any EIdException-derived exceptions you catch. You are blocking Indy's internal notifications from being dispatched and processed correctly.
(https://forums.embarcadero.com/messa...ssageID=257582)

sx2008 10. Jun 2012 03:26

AW: Indy TCPServer beenden mit toten Clients
 
Wenn eine TCP-Verbindung physisch getrennt wird, kann das keiner der beteiligten Partner erkennen ohne dass Daten gesendet werden.
Nur wer Daten sendet, kann erkennen dass die Verbindung unterbrochen ist.
Dazu gibt es in vielen Protokollen einen NOP-Befehl (NO-Operation, Befehl der nichts tut).
Man kann aber auch ein Datenpaket der Länge 0 senden, wobei das aber nicht so zuverlässig wie ein NOP-Befehl ist.

mjustin 10. Jun 2012 07:10

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von sx2008 (Beitrag 1170176)
Wenn eine TCP-Verbindung physisch getrennt wird, kann das keiner der beteiligten Partner erkennen ohne dass Daten gesendet werden.

Die Verbindung wird, wenn ich es soweit richtig verstanden habe, serverseitig zuerst logisch beendet durch senden (IOHandler.Writeln('CLOSE_REQUEST@ '), und bei dieser Operation hängt Indy noch nicht. Indy beendet den Server aber danach nicht, so dass die Anwendung nicht terminiert.

mjustin 10. Jun 2012 07:11

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von hsg (Beitrag 1170029)


Der Server versucht etwas zu senden (IOHandler.Writeln('CLOSE_REQUEST@ ');) Über diesen Punkt kommt er aber ohne Probleme hinweg.

Folgt nach dem

Delphi-Quellcode:

IOHandler.Writeln('CLOSE_REQUEST@ ')
noch ein sauberes Beenden der Verbindung? (Schliessen des Sockets)

Delphi-Quellcode:

AContext.Connection.Disconnect

Wenn das Protokoll geändert werden kann, wäre ein Heartbeat-Verfahren eventuell eine Verbesserung, damit der Server verloren gegangene Verbindungen schneller erkennt und abräumt.

Update:

unter http://www.delphipraxis.net/157267-i...er-thread.html

werden noch diese Zeilen regelmäßig im Server OnExecute ausgeführt:
Delphi-Quellcode:
    AContext.Connection.IOHandler.CheckForDisconnect(False, True);
    AContext.Connection.CheckForGracefulDisconnect(False);

himitsu 10. Jun 2012 12:23

AW: Indy TCPServer beenden mit toten Clients
 
Ich hatte soeiein Problem beim DataSnap bemerkt, welches intern auch Indy-TCP nutzt (versteckt hinter den benutzen DBConnections).

Wenn die Connection z.B. durch ein Netzwerkproblem getrennt wurde oder teilweise auch wenn Clientanwendungen abgestürzt sind, dann wurden die Connections nicht odnungsgemäß getrennt.
Der Server denkt dann die Clients seien noch vorhanden und beim Runterfahren sendet er dann allen "bekannten" Clients eine "ich bin dann mal Weg"-Nachricht, damit sie ihrerseits die Verbindung ordentlich trennen können. (ja, auch Clients können eventuell hängen, wenn der Server weg ist)

Wobei er dann einfach hängen bleibt, wenn er auf soeinen toten Client trifft.


Leider läßt sich dagegen nichts machen. (wir haben alles Mögliche versucht)




Zum Teil ist das auch ein Problem von Windows, denn dieses schließt die Ports nicht, wenn die Connection abreißt, womit Indy (im Server oder auch im Client) nicht darüber informiert wird, daß die Connection eigentlich weg ist.

Standardmäßg sendet Windows keine NOPs (oder sowas), über die "aktiven" Ports. Es gibt zwar soeine Funktion, welche aber eigentlich nie aktiv ist, oder die Zeit war nur ewig hoch eingestellt. (nicht ganz sicher ... müßte nochmal nachsehn)
Es testet also selbstständig keine Portverbindungen, womit es Windows oftmals erst nach 24 Stunden auffällt und so lange die Verbindung angeblich noch besteht.

hsg 11. Jun 2012 06:34

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von mjustin (Beitrag 1170047)

Werden Exceptions eventuell so behandelt, dass Indy sie nicht bemerkt? Indy verwendet einige (selbstdefinierte) Exceptions wie EIdConnClosedGracefully zur Steuerung:

Zitat:

You need to get rid of your exception handling, or at least re-raise any EIdException-derived exceptions you catch. You are blocking Indy's internal notifications from being dispatched and processed correctly.
(https://forums.embarcadero.com/messa...ssageID=257582)

Es gibt zwar an ein- zwei Stellen ein Exception-Handling, aber auch das wird nicht getriggert.
Es herrscht einfach nur Schweigen im Walde :(

hsg 11. Jun 2012 06:52

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von sx2008 (Beitrag 1170176)
Nur wer Daten sendet, kann erkennen dass die Verbindung unterbrochen ist.

Es wird ja vorweg gesendet (Die Close_Request-Message), trotzdem erkennt der Server nicht das er alleine auf weiter Flur steht.
Das Beenden von unterbrochenen Verbindungen geht ja zum Teil. Nur wenn das angesprochene Gerät selbst nicht mehr im Netz ist, hängt sich der Server auf. Ist das Gerät im Netz, klappt auch das Beenden ohne Probleme.

hsg 11. Jun 2012 07:36

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von mjustin (Beitrag 1170180)
noch ein sauberes Beenden der Verbindung? (Schliessen des Sockets)

Delphi-Quellcode:

AContext.Connection.Disconnect
Wenn das Protokoll geändert werden kann, wäre ein Heartbeat-Verfahren eventuell eine Verbesserung, damit der Server verloren gegangene Verbindungen schneller erkennt und abräumt.

Update:

unter http://www.delphipraxis.net/157267-i...er-thread.html

werden noch diese Zeilen regelmäßig im Server OnExecute ausgeführt:
Delphi-Quellcode:
    AContext.Connection.IOHandler.CheckForDisconnect(False, True);
    AContext.Connection.CheckForGracefulDisconnect(False);

Jein :) Wird ein Disconnect vom Server versucht, hängt sich der Thread beim Beenden des Servers auf (das Problem, welches auch im verlinkten Post beschrieben wird).
Diese Hänger habe ich dadurch beseitigen können, dass der Server auf die Abmeldung (oder nicht Abmeldung) der Clients wartet. Das Server.Active := false macht übrigens auch ein Disconnect auf die vorhandenen Clients, deswegen das Jein.

Ich habe die Zeilen von oben mal im OnExecute eingebaut, keine Veränderung.
Mal eine Auflistung der Testszenarios und deren Auswirkung:

* Client meldet sich an Server an und Server wird beendet: Alles einwandfrei, der Client meldet sich ab und der Server beendet sich sauber.

* Client meldet sich an Server an, verliert die Connection, meldet sich nach Herstellung der Netzwerkverbindung erneut an Server an. Server wird beendet: Es sind zwei Connections in der Liste aufgelistet. Eine tote und eine aktive. An beiden wird das Ende-Signal gesendet. Der Server bekommt eine Exception (Socket-Error 10054) und beendet sich dann sauber.

* Client meldet sich an Server an, verliert die Connection. Redet nicht mit dem Server nach Herstellung der Netzwerkverbindung. Server wird beendet: Die tote Connection wird beendet und der Server bekommt eine Execption (Socket-Error 10054) und beendet sich dann sauber.

* Client meldet sich an Server an, verliert die Connection und kommt nicht wieder ins Netz. Server wird beendet: Es wird die Meldung an den toten Client gesendet und Server hängt sich auf. KEINE Exception, keine andere Meldung.

Wie gesagt: Nur wenn der Client netzwerktechnisch nicht mehr erreichbar ist, hängt sich der Server komplett auf.

hsg 11. Jun 2012 07:44

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von himitsu (Beitrag 1170198)

Leider läßt sich dagegen nichts machen. (wir haben alles Mögliche versucht)

Zum Teil ist das auch ein Problem von Windows, denn dieses schließt die Ports nicht, wenn die Connection abreißt, womit Indy (im Server oder auch im Client) nicht darüber informiert wird, daß die Connection eigentlich weg ist.

Kann man da wirklich nichts gegen machen? Da unsere Leute sich recht gerne aus dem WLan-Bereich entfernen und sie auch mal gerne vergessen, die Geräte in die Ladestationen zu stellen, kommt es leider öfters zu diesen Netzwerkabbrüchen.
Der Rechner, auf dem der Server läuft, wird aus bestimmten Gründen jede Nacht neugestartet (automatisiert!) Da ist ein nicht beendenbarer Server sehr hinderlich :(

NickelM 11. Jun 2012 08:09

AW: Indy TCPServer beenden mit toten Clients
 
Abgesehen von den technischen TCP Problemen, funktioniert der automatisierter Neustart richtig?
Oder ist es schon vorgekommen, dass der Server am nächsten Tag nicht mehr Erreichbar ist, da der Server nicht beendet werden konnte?
Das klingt jetzt nach einer "Hammer auf Kopf"-Methode, aber ich würd sagen, dass du diesen Thread, falls er hängt mit TerminateThread WinAPI "crashst", oder direkt die ganze Anwendung von einer Anwendung "crashen" lässt mit TerminateProcces oder so.
Ist eine Recht unschöne Methode zum Neustart. Aber immerhin würde der Server weiterfunktionieren...zumindest am nächsten Tag :-D
Ich weis das lösst echt nicht dass Problem, aber immerhin müsstest du ihn nicht mehr von Hand neustarten.

Es wäre aber echt meine persönliche letzte Idee, um diesen Problem zuumgehen, wenn nichts anderes gehen würde.

EDIT: Ehm ja...sorry :oops: Der ganze Rechner wird ja neugestartet, also macht das kein Unterschied. :oops:

Gruß NickelM

taveuni 11. Jun 2012 10:22

AW: Indy TCPServer beenden mit toten Clients
 
Ich hab jetzt nicht alles durchgelesen aber:

Kannst Du Dich von Indy lösen und ICS oder Synapse verwenden?
Bei ICS gibts die Methode CloseDelayed. Da werden alle Verbindungen
geschlossen auch die von nicht mehr verbundenen Client Klassen.
Und da hängt auch nichts.

hsg 11. Jun 2012 12:20

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von taveuni (Beitrag 1170281)
Kannst Du Dich von Indy lösen und ICS oder Synapse verwenden?

Werde mir beides ansehen.

mjustin 12. Jun 2012 12:01

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von hsg (Beitrag 1170256)
Zitat:

Zitat von himitsu (Beitrag 1170198)

Leider läßt sich dagegen nichts machen. (wir haben alles Mögliche versucht)

Zum Teil ist das auch ein Problem von Windows, denn dieses schließt die Ports nicht, wenn die Connection abreißt, womit Indy (im Server oder auch im Client) nicht darüber informiert wird, daß die Connection eigentlich weg ist.

Kann man da wirklich nichts gegen machen? Da unsere Leute sich recht gerne aus dem WLan-Bereich entfernen und sie auch mal gerne vergessen, die Geräte in die Ladestationen zu stellen, kommt es leider öfters zu diesen Netzwerkabbrüchen.
Der Rechner, auf dem der Server läuft, wird aus bestimmten Gründen jede Nacht neugestartet (automatisiert!) Da ist ein nicht beendenbarer Server sehr hinderlich :(

Es ist bei TCP völlig normal, dass der Server nicht erfährt wenn ein Client nicht mehr im Netzwerk ist. Der Indy TCP Server kann damit auch problemlos umgehen, und sich sauber beenden, ohne dass die Clients dazu noch irgendetwas tun müssen.

Ist das Problem auch mit der aktuellen Indy 10.5.8 Version nachvollziehbar?

Um die Ursache einzugrenzen würde ich eine Testversion der Anwendung bauen, in der das Problem ohne Zugriff auf externe Hardware nachvollziehbar ist, durch einen "simulierten" Client. Danach würde ich die Anwendung so weit reduzieren wie es möglich ist und der Fehler noch nachvollziehbar ist.

Zitat:

* Client meldet sich an Server an, verliert die Connection und kommt nicht wieder ins Netz. Server wird beendet: Es wird die Meldung an den toten Client gesendet und Server hängt sich auf. KEINE Exception, keine andere Meldung.
Wenn es kein WLAN wäre, entspricht das "Anmelden, dann Netzwerkkabel des Clients entfernen", oder einfach "Anmelden, dann den Client abschalten".

Zitat:

* Client meldet sich an Server an, verliert die Connection, meldet sich nach Herstellung der Netzwerkverbindung erneut an Server an. Server wird beendet: Es sind zwei Connections in der Liste aufgelistet. Eine tote und eine aktive. An beiden wird das Ende-Signal gesendet. Der Server bekommt eine Exception (Socket-Error 10054) und beendet sich dann sauber.
Bei welcher der beiden Connections kommt die Exception? Seltsam, dass das zweite Anmelden dazu führt, dass die erste (dann "tote") Verbindung beim Herunterfahren keinen Hänger mehr auslöst - und wenn das zweite Anmelden nicht erfolgt, der Hänger auftritt.

hsg 12. Jun 2012 12:28

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von mjustin (Beitrag 1170414)
Es ist bei TCP völlig normal, dass der Server nicht erfährt wenn ein Client nicht mehr im Netzwerk ist. Der Indy TCP Server kann damit auch problemlos umgehen, und sich sauber beenden, ohne dass die Clients dazu noch irgendetwas tun müssen.

Ist das Problem auch mit der aktuellen Indy 10.5.8 Version nachvollziehbar?

Sorry, dass habe ich verschwitzt zu testen. Bin gestern und heute eh noch nicht dazu gekommen, an dem Problem effektiv herumzutesten.


Zitat:

Zitat von mjustin (Beitrag 1170414)
Um die Ursache einzugrenzen würde ich eine Testversion der Anwendung bauen, in der das Problem ohne Zugriff auf externe Hardware nachvollziehbar ist, durch einen "simulierten" Client. Danach würde ich die Anwendung so weit reduzieren wie es möglich ist und der Fehler noch nachvollziehbar ist.

Das ist kein Problem :-D
Der Testclient ist der WindowsMobile-DeviceEmulator, dem klaue ich mit einem Tastendruck das Netzwerk und gebe es ihm wieder, der Server ist (bis auf die Execute-Methode und dem notwendigen Beiwerk auch relativ schlank.

Zitat:

Zitat von mjustin (Beitrag 1170414)
Wenn es kein WLAN wäre, entspricht das "Anmelden, dann Netzwerkkabel des Clients entfernen", oder einfach "Anmelden, dann den Client abschalten".

Korrekt. So ist der Stresstest bei mir zur Zeit ja auch aufgebaut. Der DeviceEmulator spielt ja seine Netzwerkverbindung über ein simuliertes WLan.

Zitat:

Zitat von mjustin (Beitrag 1170414)
Zitat:

* Client meldet sich an Server an, verliert die Connection, meldet sich nach Herstellung der Netzwerkverbindung erneut an Server an. Server wird beendet: Es sind zwei Connections in der Liste aufgelistet. Eine tote und eine aktive. An beiden wird das Ende-Signal gesendet. Der Server bekommt eine Exception (Socket-Error 10054) und beendet sich dann sauber.
Bei welcher der beiden Connections kommt die Exception? Seltsam, dass das zweite Anmelden dazu führt, dass die erste (dann "tote") Verbindung beim Herunterfahren keinen Hänger mehr auslöst - und wenn das zweite Anmelden nicht erfolgt, der Hänger auftritt.

Es ist die tote Verbindung, bei der die Exception auftaucht.
Wie gesagt, dass Problem besteht nur wirklich dann, wenn die Netzwerkverbindung nicht vorhanden ist. Also das Gerät auch mittels Ping nicht erreichbar ist.
Der Server lässt sich sauber beenden, wenn die physikalische Netzwerkverbindung zwischen Server-Rechner und Client-Rechner besteht. Besteht diese Verbindung nicht, hängt der Server.
Ich hoffe, so ist es ein wenig klarer geworden.

So, nun werde ich meine Delphi-Umgebung zerstören in dem ich die aktuelle Indy-Version einspiele :-D

mjustin 12. Jun 2012 13:12

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von hsg (Beitrag 1170424)
So, nun werde ich meine Delphi-Umgebung zerstören in dem ich die aktuelle Indy-Version einspiele :-D

Sicherheitshalber installiere ich Indy nie als Komponenten in der IDE, das dauert eh viel zu lange - stattdessen setze ich nur für das Projekt die Pfade Indy/Lib/Core, Prtotocols und System (und lasse die Orignalversion von Indy installiert). Aber "zerstören" ist noch ein zu sanftes Wort für den Indy-Effekt, den ich auch schon kennenlernte :)

hsg 12. Jun 2012 14:48

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von mjustin (Beitrag 1170436)
Zitat:

Zitat von hsg (Beitrag 1170424)
So, nun werde ich meine Delphi-Umgebung zerstören in dem ich die aktuelle Indy-Version einspiele :-D

Sicherheitshalber installiere ich Indy nie als Komponenten in der IDE, das dauert eh viel zu lange - stattdessen setze ich nur für das Projekt die Pfade Indy/Lib/Core, Prtotocols und System (und lasse die Orignalversion von Indy installiert). Aber "zerstören" ist noch ein zu sanftes Wort für den Indy-Effekt, den ich auch schon kennenlernte :)

Wie installierst du denn das Zeug?
Wenn ich das richtig sehe, muss ich ja dieses Fulld10.bat (BDS2006) aufrufen, aber dann installiert der ja alles in irgendwelchen vordefinierten Verzeichnissen, oder?
Ein ReadMe im Zip-File wäre nett gewesen :(
Wenn du nur die Pfade setzt, wie stellst du sicher, dass nicht die fertigen Pakete dir in die Suppe spucken?

Für heute gebe ich erst mal auf, denn es ist endlich Feierabend! (und wieder nichts geschafft :( )

mjustin 12. Jun 2012 16:11

AW: Indy TCPServer beenden mit toten Clients
 
Zitat:

Zitat von hsg (Beitrag 1170471)
Wie installierst du denn das Zeug?
Wenn ich das richtig sehe, muss ich ja dieses Fulld10.bat (BDS2006) aufrufen, aber dann installiert der ja alles in irgendwelchen vordefinierten Verzeichnissen, oder?
Ein ReadMe im Zip-File wäre nett gewesen :(
Wenn du nur die Pfade setzt, wie stellst du sicher, dass nicht die fertigen Pakete dir in die Suppe spucken?

Nach dem Entpacken hat man die Quelltexte in den Verzeichnisse /Indy10.5.8/Lib/Source, Protocols und System. Wenn Delphi diese in den Projektsuchpfaden findet, werden die installierten Indy Packages nicht verwendet und es gibt keine Konflikte. (Ausser in den Fällen in denen man DCU Dateien ohne Source in den Pfaden hat, die mit einer anderen Indy Version kompiliert wurden).

Wenn in allen meinen Projekten keine Designtimekomponenten von Indy (oder davon abhängige andere Komponenten) benutzt werden, kann ich die Indy Packages auch deinstatallieren, zumindest in den installierten Packages abwählen.

hsg 13. Jun 2012 05:57

AW: Indy TCPServer beenden mit toten Clients
 
So, habe nun die neuere Version der Indys probiert. Leider das gleiche Ergebnis.

Ich werde mir jetzt mal das ganze mit dem ICS-Zeugs ansehen. Leider ist die Dokumentation von den Komponenten mehr als dürftig :-( Wenn also noch jemand ein paar Links hat, immer her damit!

taveuni 13. Jun 2012 08:02

AW: Indy TCPServer beenden mit toten Clients
 
Da hats massig Beispiele dabei (OverbyteIcsV7\Delphi\Internet\).
Ausserdem eine aktive Newsgroup.

DataCool 13. Jun 2012 08:21

AW: Indy TCPServer beenden mit toten Clients
 
Hi,

wäre es möglich Deinen gesamten Code des OnExecute Events des Servers zu posten ?
Ich habe auch schon lange mit den Problem gekämpft, habe aber jetzt keinerlei Probleme!
Man muss nur ein Paar Sachen beachten:

- GANZ GANZ wichtig keine Exception schlucken, diese müssen zwingend im OnExecute auch auftreten,
ansonsten merkt "die kontrollierende Instanz" von Indy nicht, das überhaupt ein Fehler aufgetreten ist;
Erst beim nächsten WriteLn/ReadLn

- Bitte füge mal folgenden Code ganz oben im OnExecute ein:
Delphi-Quellcode:
  AContext.Connection.IOHandler.CheckForDataOnSource(500); // millisekunden, nach Bedarf varieren
  if not AContext.Connection.Connected then exit;
  if not AContext.Connection.IOHandler.InputBufferIsEmpty then
  begin
    // hier Deinen ganz normalen Ablauf implementieren
  end
  else begin
    // Keine Daten im InputBuffer; theoretisch muss nichts gemacht werden; auch kein SLEEP !
    //
    // hier könnte man gut überprüfen, wann der letzte "Kontakt"/Datenaustausch mit dem Client war
    // ist dieser länger her als Dein definierter Timeout, dann einfach etwas in die Connection schreiben
    // das kann ein "NOOP" Kommando sein, aber auch ein Kommando auf das der Client gar nicht kennt
    // das Schreiben dient nur dazu um wirklich eine Exception auszulösen, die Indy dann handeln kann.
    // Bestehend die Connection noch kommt das Kommando normal beim Client an und
    //kann behandelt oder ignoriert werden
    AContext.connection.Socket.WriteLn('NOOP');
    // oder:
    // AContext.connection.Socket.WriteLn('STFU'); // ;-)
  end;
- Des Weiteren ist es sehr zu empfehlen, wie auch schon mehrfach vorher erwähnt wurde,
das Protokoll so aufzubauen das es ein "NOOP" Kommando gibt,
ebenso ein "QUIT/EXIT" Kommando

- Ich für meinen Teil gehe immer so wat das ich mit ein TClient-Objekt erstelle und diese in der Eigenschaft
AContext.Data mitführe. Zusätzlich zu den Eigenschaft die das TClient-Objekt definieren,
führe ich immer noch eine Property LastContact oder LastTimeStamp oder whatever mit,
die mir die Information liefert, wann zum letzten mal Erfolgreich Daten gelesen oder geschrieben wurden.

Wenn das alles beherzigt wird, gibt es keinerlei Probleme mit "Zombie" Clients.

Greetz Data

hsg 13. Jun 2012 10:26

AW: Indy TCPServer beenden mit toten Clients
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von DataCool (Beitrag 1170574)
Hi,

...
Wenn das alles beherzigt wird, gibt es keinerlei Probleme mit "Zombie" Clients.

Greetz Data

Hi,

ich habe die (reduzierte) Datei mal angehängt. Deine Hinweise sind entsprechend eingebaut.
Im gesamten Programm werden im Moment nur ganz wenige Exceptions abgefangen (und auch nur an den dringendsten Stellen).

Wir führen bereits ein TClient-Objekt mit, allerdings ohne TimeStamp oder dergleichen.
Das Noop-Kommando habe ich jetzt für den Versuch mit eingebaut, wird aber in der Realität nicht möglich sein, da es ein paar ältere Clients gibt, die mit den NOOP-Meldungen nicht zurande kommen werden (Update auf neue Version wahrscheinlich nicht möglich)

Ich weise noch mal (vorsichtshalber) darauf hin: Es gibt nur dann Probleme, wenn der Clientrechner im Netzwerk (physikalisch) nicht mehr erreichbar ist.
Egal, ob Clientprogramm läuft oder nicht.

Das ist auch in der jetzigen Version so.

Bei Bedarf (und jemand sich mit dem netten DeviceEmulator von Microsoft auskennt) kann ich auch den Client zur Verfügung stellen Damit kann man dann recht elegant das Phänomen deutlich machen.

DataCool 13. Jun 2012 11:21

AW: Indy TCPServer beenden mit toten Clients
 
Liste der Anhänge anzeigen (Anzahl: 1)
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

hsg 14. Jun 2012 05:59

AW: Indy TCPServer beenden mit toten Clients
 
Hallo Data,

Zitat:

Zitat von DataCool (Beitrag 1170629)
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.

Es war nur ein kleiner Fehler drin :)

Zitat:

Zitat von DataCool (Beitrag 1170629)
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 !!!

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.

Ich habe ein paar Try-Excepts rausgeworfen gehabt, damit ich sicherstellen konnte, dass nicht alle Exceptions abgefangen werden und nicht mehr durchgereicht werden. Mit dem Try...finally hast du natürlich recht.

Das DisconnectAllClients wird vom Hauptfenster aufgerufen. Ich hatte es ursprünglich mal an der SetActive-Stelle drin, gab dann aber Probleme beim Beenden auch unter normalen Bedingungen. Das Application.ProcessMessages ist an der Stelle entscheidend, denn ansonsten können die Clients sich nicht erfolgreich abmelden. Das gesamte Programm ist so geschrieben, dass es sowohl als Dienst als auch als Fenster-Programm kompiliert werden kann. Daher habe ich in der uPdaServer.pas kein definiertes Applikations-Fenster zur Verfügung und deswegen den entsprechenden Aufruf von DisConnectAllClients in die Verwaltungsroutine geschoben.

Delphi-Quellcode:
procedure TMainForm.StopServer;
var
  i: Integer;
begin
  if oServer <> nil then begin

    mleInfo.Lines.Add('Warten auf Abmeldung Clients');
    oServer.DisConnectAllClients();
    for i := 0 to 3 do begin
      Sleep(1000);
      Application.ProcessMessages();
    end; // for i := 0 to
    mleInfo.Lines.Add('Server wird gestoppt');

    oServer.Active                 := False;
    FreeAndNil(oServer);

    btnClearLog.Click;
    btnServer.Tag                  := 0;
    btnServer.Caption              := 'Server starten';
    btnGetClientList.Enabled       := False;
  end;
end;
Zitat:

Zitat von DataCool (Beitrag 1170629)
Das sollte Dein Problem jetzt aber lösen (hoff ich)

Greetz Data

leider nein :(

Gruß
hsg

Edit:
iClientCount ist beim oServer.Active := false größer als Null. Der TimeOut ist in deiner Schleife die Abbruchbedingung.


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:42 Uhr.

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