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 TCPClient Connection Status (https://www.delphipraxis.net/184346-indy-tcpclient-connection-status.html)

Delphi-Narr 19. Mär 2015 10:42

Indy TCPClient Connection Status
 
Hallo,
ich hatte vor einiger Zeit ein Client-Server-Konstrukt gebastelt, mit dem mehrere Rechner Systeminformationen (CPU, RAM etc) an einen Server schicken.
Die Clients sind alle Windows Rechner, der Server wurde jetzt auf Linux umgestellt, sodass ich den Server mit Lazarus auf Linux kompiliert habe.
Zu Testzwecken ist der Server erstmal nur eine Konsolenanwendung und noch kein Dienst oder ähnliches.

Der Server erstellt einen Indy10 TCP-Server und geht dann in eine "Endlosschleife", in welcher er wartet.

Delphi-Quellcode:
procedure TMyServer.DoRun;
begin
  Init();
  while (not Terminated) do
  begin
    if KeyPressed then
      if ReadKey = ^C then
      begin
           Log('Shutting down...');
           Terminate;
      end;
    Sleep(2);
  end;
  Terminate;
end;
Die Initialisierung klappt auch wunderbar, der Server ist bereit, Verbindungen entgegenzunehmen.
Der "alte" Windows Client kann sich auch zum Server verbinden und Daten übertragen. Nach ca. 1-2 Sekunden stellt er jedoch immer eine neue Verbindung zum Server her.
Dies ist laut Client auch vorgesehen, wenn er keine aktive Verbindung feststellt:

Delphi-Quellcode:
if (TCPClient.Connected) then
   //Irgendwas sinnvolles
else
   //Reconnect
Nur stellt er in einer TimerLoop anscheinend jedesmal fest, dass TCPClient.Connected = false ist.

Im Server wird die
Delphi-Quellcode:
TCPServerDisconnect
anscheinend nicht aufgerufen, der Server kriegt den Disconnect nicht mit.

Woran könnte das liegen? :?:

Viele Grüße!

himitsu 19. Mär 2015 10:48

AW: Indy TCPClient Connection Status
 
Zitat:

Konsolenanwendung ...
Wenn die komponenten mit Messages arbeiten, dann mußt du auch Messages verarbeiten (die VCL macht das konstruktionsbegingt automaisch), sonst kann da schnell einiges nicht (richtig) funktionieren.



PS: Bei Verbindungsabbrüchen kann es sein, daß Connected noch lange True liefert.
Der einzige sichere Weg eine Verbindung zu prüfen, ist etwas zu senden und auf die Antwort zu warten.
(Connected=False ist dagegen natürlich immer Disconnected)

Delphi-Narr 19. Mär 2015 10:56

AW: Indy TCPClient Connection Status
 
Ah, ja das hatte ich mir schon gedacht. Unter Lazarus habe ich das ganze aber nicht hinbekommen, da alles, was ich dazu gefunden habe auf Windows Bibliotheken basiert.
Ich müsste also in der Loop die Messages manuell abfragen, wie könnte das unter Linux denn gehen?

Wenn connected noch true liefert, schickt der Client die Daten an den Server und erhält eine Bestätigung. Aber selbst, wenn die Daten verloren gehen und erst nach 10 Sekunden neu geschickt würden, wenn der Client mitbekommen hat, dass die Verbindung tot ist, wäre kein Problem (An der Hardware-Konfiguration ändert sich ja nicht so viel ;) )

Alle 30 Sekunden tauschen Server und Client auch unabhängig voneinander Keep-Alive-Meldungen aus und trennen die Verbindung, wenn die nicht ankommen.

mjustin 19. Mär 2015 11:13

AW: Indy TCPClient Connection Status
 
Zitat:

Zitat von Delphi-Narr (Beitrag 1294050)
Ich müsste also in der Loop die Messages manuell abfragen, wie könnte das unter Linux denn gehen?

Indy verwendet keine Windows Messages - daher war ist es leichter auf andere Betriebssystem portierbar. Der Fehler muss also woanders liegen.

Ich würde mal einen Client auf dem gleichen Linux System laufen lassen um zu testen, ob er sich auch ständig neu verbindet.

Und auch mit netstat mal schauen ob und wielange der Serverport geöffnet ist.

Delphi-Narr 20. Mär 2015 10:31

AW: Indy TCPClient Connection Status
 
Der Client ist in diesem Fall nicht Linux kompatibel und lässt sich auch nicht so leicht portieren...

Laut nmap mit watch ist der Port die ganze Zeit offen. Ich denke, dass der Client die "connected"-Abfrage immer zu false auswertet... Eine Disconnect Fehlermeldung kommt nicht, nur alle paar Sekunden die Verbunden-Meldung.

mjustin 20. Mär 2015 10:54

AW: Indy TCPClient Connection Status
 
Zitat:

Zitat von Delphi-Narr (Beitrag 1294207)
Ich denke, dass der Client die "connected"-Abfrage immer zu false auswertet

Was ein Argument dafür ist, sie wegzulassen ;)

In meinen Indy-basierten Clients benutze ich heart-beat Nachrichten um den Verbindungsstatus zu prüfen. Ob die Verbindung noch besteht läßt sich dadurch prüfen, dass man versucht Daten zu empfangen (IOHandler.Readxxx) oder zu senden (IOHandler.Writexxx). Falls dann eine Exception auftritt, verwirft der Client die Verbindung, und versucht sie wiederherzustellen, am sichersten indem auch eine neue Instanz der TIdTCPClient Klasse erzeugt wird.

Delphi-Narr 20. Mär 2015 11:01

AW: Indy TCPClient Connection Status
 
Ja das werde ich wohl tun ;)
Heartbeats werden auch verschickt, nur nicht so oft. Das werde ich dann mal anpassen.
Komisch, dass das bei Windows-"interner" Kommunikation die Abfrage das richtige Resultat lieferte.

Delphi-Narr 24. Mär 2015 10:55

AW: Indy TCPClient Connection Status
 
Habe hierzu nochmal eine Frage:
Ich habe jetzt den Heartbeat eingebaut, der Client schickt in einem Intervall die Heartbeats und der Server antwortet.
In der ContextClass des Servers werden die MacAdresse und der Zeitpunkt des letzten Heartbeats des Clients gespeichert. In festen Intervallen räumt der Server dann auf, d.h. er durchläuft die Liste der verbundenen Clients und alle, die zu lange keinen Heartbeat mehr hatten, werden disconnected - zumindest in der Theorie.
Der Client erkennt inzwischen korrekt, ob er keine Verbindung mehr hat, der Server vom Prinzip her auch.

Delphi-Quellcode:
procedure TMyServer.HeartbeatCheck();
var
    ContextList:TList;
    Context:TIdContext;
    i:integer;
begin
    ContextList := TCPServer.Contexts.LockList;
    try
       for i := 0 to ContextList.Count-1 do
           begin
                Context := TIdContext(ContextList.Items[i]);
                if (IncSecond(TClientIdentifier(Context).Heartbeat, CLIENTTIMEOUT) < Now) and (TClientIdentifier(Context).Heartbeat <> 0) then
                   begin
                        Log('Client '+TClientIdentifier(Context).Mac+' timed out', ltE);
                        TIdContext(ContextList.items[i]).Connection.IOHandler.close;
                        TIdContext(ContextList.items[i]).Connection.Disconnect();
                   end;
           end;
     finally
        TCPServer.Contexts.UnlockList;
     end;
end;
Wenn ein Client die Verbindung verliert, wird das entsprechend geloggt, doch in JEDEM Durchlauf wird er erneut als timed out erkannt - dabei sollte er ja eigentlich aus der Liste gelöscht werden?!
Und auch wenn der Client die Verbindung wieder aufnimmt und Heartbeats schickt, wird er dennoch wieder getrennt.
Im OnDisconnect des Servers wird eigentlich ebenfalls geloggt, dass ein bestimmter Client die Verbindung getrennt hat - dies wird in der Heartbeat Methode aber offenbar gar nicht ausgelöst. Vielleicht ist es ja nur ein ganz einfacher blöder Fehler ;)

Viele Grüße!

mjustin 24. Mär 2015 15:25

AW: Indy TCPClient Connection Status
 
Zitat:

Zitat von Delphi-Narr (Beitrag 1294611)
In festen Intervallen räumt der Server dann auf, d.h. er durchläuft die Liste der verbundenen Clients und alle, die zu lange keinen Heartbeat mehr hatten, werden disconnected - zumindest in der Theorie.

Auf Anhieb kann ich keinen Fehler erkennen. Frage: funktioniert es unter Windows, aber unter Linux nicht? Oder auf beiden System nicht?

Als Alternative würde ich noch erwägen die Heartbeat-Prüfung nicht als Schleife über alle Context Objekte zu realisieren sondern innerhalb der OnExecute - Loop. Damit spart man sich das LockList, da man in OnExecute den zu prüfenden Context threadsicher untersuchen kann. (Auf diese Idee hat mich ein Beitrag von Remy Lebeau gebracht: http://stackoverflow.com/a/1534800/80901).

In der OnExecute Loop kann man dann einfach eine (Indy-) Exception auslösen wenn der aktuelle Kontext zu lange kein Heartbeat-Signal mehr vom Client sah.

Ausgehend von diesem Codeschnipsel (http://stackoverflow.com/a/14180366/80901):

Delphi-Quellcode:
procedure TMainForm.IdTCPServerExecute(AContext: TIdContext);
var
  ...
begin
  ...

  if AContext.Connection.IOHandler.InputBufferIsEmpty then
  begin
    if not AContext.Connection.IOHandler.CheckForDataOnSource(100) then
    begin
      AContext.Connection.IOHandler.CheckForDisconnect;
      Exit;
    end;
  end;

  TCliContext(AContext).ProccessMsg;
  TCliContext(AContext).Activity_time := Now();
end;
... würde man nach dem CheckForDataOnSource auch die Activity_time prüfen und - falls sie "zu alt" ist - eine TIdException auslösen anstatt die Methode mit Exit zu verlassen. Durch die Exception erfährt der Server, dass er den Context freigeben und die Verbindung trennen soll.

Delphi-Narr 24. Mär 2015 15:38

AW: Indy TCPClient Connection Status
 
Unter Windows habe ich das nicht getestet, unter Linux scheint der Eintrag auf jeden Fall nicht korrekt gelöscht werden.
Das OnExecute wird doch nur ausgeführt, wenn was vom Client kommt oder nicht? Wenn also kein Client mehr irgendetwas auslöst - weil die Verbindung getrennt wurde - kann der disconnect dann überhaupt erkannt werden?
Das Problem ist ja nicht, den Context zu untersuchen, sondern den zugehörigen Client zu löschen.
Ich werde die Lösung auf jeden Fall mal testen :)


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