Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi IdTCPServer: RSTerminateThreadTimeout (Terminate Thread ...) (https://www.delphipraxis.net/57141-idtcpserver-rsterminatethreadtimeout-terminate-thread.html)

Puhbaehr 16. Nov 2005 13:16


IdTCPServer: RSTerminateThreadTimeout (Terminate Thread ...)
 
Hallo!

Ich stehe wieder mal vor einem Problem bei dem ich nicht weiter komme:

Zur Vorgeschichte: Ich erstellte zurvor einen IdTCPServer und IdTCPClienten von Indy in einer eigenen Unit.
Da es da aber viele Fehler gab hab ich, um die Fehler leichter zu debuggen schnell die Unit in ein neues Formular geschrieben.

Die Indy-Komponenten sind in einer eigenen Komponente zusammengefasst.

Delphi-Quellcode:
type
  TTCPIP = class (TComponent)
    private              
      TCPServer : TIdTCPServer;
      TCPClient : TIdTCPClient;
      ...
    published
      constructor Create (Owner : TComponent); override;
      destructor Destroy; override;
                           
      procedure CreateTCPServer;
      procedure DestroyTCPServer;
     
      procedure CreateTCPClient;  
      procedure DestroyTCPClient;
      ...
Die Klasse TTCPIP stand zuvor allein da und binde ich nun mit:

Delphi-Quellcode:
type
  TForm1 = class(TForm)
    ...
    published
      TCPConnection : TTCPIP;
    ...

...


var
  Form1: TForm1;
in das Formular ein.

Beim Erstellen des Formulars erstelle ich auch die Komponente:

Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
 begin  
  TCPConnection := TTCPIP.Create (Application);
 end;
Im Constructor Create von der TTCPIP-Komponente erstelle ich Server und Client mit:

Delphi-Quellcode:
constructor TTCPIP.Create (Owner : TComponent);
 begin
  inherited Create (Owner);
  ...                              
  CreateTCPServer;
  CreateTCPClient;
 end;

...

procedure TTCPIP.CreateTCPServer;
 begin                        
  TCPServer := TIdTCPServer.Create (Form1.TCPConnection);   // ist das Form1.TCPConnection im Create richtig?

  with TCPServer do
   begin
    OnConnect := TCPServerOnConnect;
    ...
    OnListenException := TCPServerOnListenException;
   end;  

  ServerConnected := false;
 end;  
 
procedure TTCPIP.CreateTCPClient;
 begin                                  
  TCPClient := TIdTCPClient.Create (Form1.TCPConnection);
 
  with TCPClient do
   begin
    OnConnected := TCPClientOnConnected;
    ...
    OnStatus := TCPClientOnStatus;
   end;
         
  ClientConnected := false;
 end;
Auf dem Form hab ich zwei Buttons, einer zum Verbinden, der andere zum Trennen.
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
 begin  
  with TCPConnection do
   begin
    ServerIP := '0.0.0.0';
    ...
    Timeout := 1;

    StartServer;
    ConnectToServer;
   end;  
 end;

procedure TForm1.Button2Click (Sender: TObject);
 begin    
  with TCPConnection do
   begin  
    DisconnectToServer;

    EndServer;  
   end;    
 end;
Zusätzlich zwei Memos in denen ich im einem Fehlermeldungen ausgeben lasse und im anderen "neutrale" (also auf deutsch: gute) Meldungen.
...

Beim Klick auf Button1 wird erst der Server gestartet und Anschließend der Client zum Server verbunden:

Delphi-Quellcode:
 
procedure TTCPIP.StartServer;
var
  Binding : TIdSocketHandle;
 begin  
  if (ServerConnected) then
   begin
    ErrorMessage := 'Server bereits gestartet';
    exit;
   end;
 
  try
    try
      Binding := TCPServer.Bindings.Add;
      Binding.IP := FServerIP;
      Binding.Port := FServerPort;  
   
      TCPServer.Active := true;

      ServerConnected := TCPServer.Active;
    except
      on E : Exception do
       begin
        ErrorMessage := 'Fehler beim Starten des Servers: ' + E.Message;
       end;
     end;
   finally
     Message := 'Server erfolgreich gestartet';
    end;  

  if (not (ServerConnected)) then
   begin
    ErrorMessage := 'Server nicht gestartet';
   end;
 end;

procedure TTCPIP.ConnectToServer;
 begin  
  TCPClient.Host := FClientIP;
  TCPClient.Port := FClientPort;
 
//  TCPClient.ReadTimeout := Timeout * 1000;

  try
    try
      TCPClient.Connect (Timeout * 1000);
    except
      on E : Exception do
       begin
        ErrorMessage := 'Fehler beim Verbinden zum Server: ' + E.Message;
       end;
     end;
  finally
    Message := 'Client verbunden';
   end;                    
   
  ClientConnected := TCPClient.Connected;
 end;
Bis dahin ist alles in Ordnung.

Beim Klick auf den zweiten Button wird der Client vom Server getrennt und der Server beendet:

Delphi-Quellcode:
procedure TTCPIP.DisconnectToServer;
 begin
  if (not ClientConnected) then
   begin
    ErrorMessage := 'Client bereits getrennt';
   end;

  try
    try
      TCPClient.Disconnect;
    except
      on E : Exception do
       begin
        ErrorMessage := 'Fehler beim Trennen vom Server: ' + E.Message;
       end;
     end;    
  finally
    Message := 'Client erfolgreich getrennt';
   end;
   
  ClientConnected := TCPClient.Connected;
 end;

procedure TTCPIP.EndServer;
 begin  
  TCPServer.Active := false;   // Und hier kracht es.
  TCPServer.Bindings.Clear;
 
  ServerConnected := (not (TCPServer.Active));

  if (ServerConnected) then
   begin
    ErrorMessage := 'Server nicht beendet';
   end
  else
   begin
    Message := 'Server erfolgreich beendet';
   end;
 end;
Beim setzen von TCPServer.Active auf false hängt das Form erst einige Sekunden und hängt sich anschließend hier (in der Unit IdTCPServer) in dieser Prozedur:

Delphi-Quellcode:
procedure TIdTCPServer.TerminateAllThreads;
const
  LSleepTime: Integer = 250;
var
  i: Integer;
  LThreads: TList;
  LTimedOut: Boolean;
begin
  // Threads will be nil if exception happens during start up, such as trying to bind to a port
  // that is already in use.
  if Assigned(Threads) then begin
    // This will provide us with posibility to call AThread.Notification in OnDisconnect event handler
    // in order to access visual components. They can add notifications after the list has been
    // unlocked, and before/while TerminateThreads is called
    LThreads := Threads.LockList; try
      for i := 0 to LThreads.Count - 1 do begin
        with TIdPeerThread(LThreads[i]) do begin
          Connection.DisconnectSocket;
        end;
      end;
    finally Threads.UnlockList; end;
    // Must wait for all threads to terminate, as they access the server and bindings. If this
    // routine is being called from the destructor, this can cause AVs
    //
    // This method is used instead of:
    //  -Threads.WaitFor. Since they are being destroyed thread. WaitFor could AV. And Waiting for
    //   Handle produces different code for different OSs, and using common code has troubles
    //   as the handles are quite different.
    //  -Last thread signaling
    // ThreadMgr.TerminateThreads(TerminateWaitTime);

    if not TIdThreadSafeList(Threads).IsCountLessThan(1) then begin
      LTimedOut := True;
      for i := 1 to (TerminateWaitTime div LSleepTime) do begin
        Sleep(LSleepTime);
        if TIdThreadSafeList(Threads).IsCountLessThan(1) then begin
          LTimedOut := False;
          Break;
        end;
      end;
      if LTimedOut then begin      
        raise EIdTerminateThreadTimeout.Create(RSTerminateThreadTimeout);   // <-- hier
      end;
    end;
  end;
End;//TerminateAllThreads
...mit raise EIdTerminateThreadTimeout.Create(RSTerminateThread Timeout); durch die Exception aus.

Aber warum ich die Exception bekomme weiß ich nicht. Und genau dazu brauch ich Hilfe.

Daten werden bisher nicht an den Server gesendet und auch der OnExecute-Thread des Servers ist vorerst leer.

Da ich mit dem Problem bereits über eine Woche zu kämpfen hab bin ich über jede Hilfe die ich kriegen kann dankbar!
Ich denke bald der Fehler liegt beim Erstellen (Create) des Servers oder des Clienten.

Delphi-Version: Delphi 5 Enterprise
Indy-Version: 9

Vielen Dank und Gruß, Robert

Puhbaehr 16. Nov 2005 20:44

Re: IdTCPServer: RSTerminateThreadTimeout (Terminate Thread
 
Mh hat denn keiner ne Idee? Oder schrecken alle vor dem vielen Code zurück? :) Könnt ich mir gut vorstellen :gruebel: ist ziemlich lang. Aber der Vollständigkeithalber ging es nicht anders.

Ok, machen wir es anders. Weiß jemand warum ein RSTerminateThreadTimeout aufgerufen wird?

Noch eine andere Frage die das Problem evtl. lösen könnte: Wenn ich den Server in meine eigene Komponente einbinde und diese wiederrum in die Typ-Deklaration den Forms wird dann beim erstellen des Forms mein Server automatisch auch erstellt?
Wenn ja, dann könnte ja sein, dass ich durch mein zusätzliches Create den Server und Client zweimal erstelle und TerminateAllThreads wartet bis sämtliche "aufgelöst" sind...oder so ...hm aber mit dem Create erstell ich doch keinen Thread? Oder doch? :wall: Ich probiers aus...

Edit: Ok an nem doppeltem Create kanns nicht liegen da er bei auskommentieren des Creates meckert. Also war die Vermutung falsch...Also heißts weiter suchen...

etom291272 16. Nov 2005 21:29

Re: IdTCPServer: RSTerminateThreadTimeout (Terminate Thread
 
was hat das für einen vorteil wenn du die beiden indy compos in eine eigene Klasse kapselst ?

Hab schon einiges mit indy gemacht serverform IdTcpServer drauf auf den Client IdTcpClient Propertys einstellen code schreiben was er machen soll und geht.

Puhbaehr 16. Nov 2005 21:35

Re: IdTCPServer: RSTerminateThreadTimeout (Terminate Thread
 
Nunja, der Server und Client war zuvor Formlos, d.h. ohne Formular und sollte praktisch "blind" bzw. unsichtbar laufen. Deswegen gab es keine Komponente von der Form und hab eine eigene gebastelt.
Da ich aber debuggen musste hab ich schnell ein Form dazu gezaubert. Sobald alles funktioniert werd ich das Form evtl. wieder weg nehmen.
Aber ich werd mal, wenn du den gedankenstoß schon gibst, alles in die TForm-Klasse schreiben. Vielleicht hilfts oder es wird dabei nen Fehler sichtbar :gruebel:

Seltsamerweise funktionierts prima wenn ich den TCPServer und TCPClient aufs Formular ziehe und damit arbeite. Aber nicht mit dem selbst erstelltem :|

Was genau geb ich als Parameter dem Create des IdTCPServers mit wenn es als eigene Komponente in der Formkomponente arbeitet? Das Form? Oder Self?

Ultimator 16. Nov 2005 21:38

Re: IdTCPServer: RSTerminateThreadTimeout (Terminate Thread
 
Hmm, also dem Code nach, der im Indy-Quelltext steht zu urteilen, werden ja erst alle Clients nacheinander vom Server getrennt. Dann wird geschaut, ob in der (threadsicheren) Clientliste noch Einträge vorhanden sind. Falls ja, wird gewartet, und dann nochmal versucht, die verbleibenden Clients zu trennen. Wenn dann immernoch welche da sind, dann wird darauf geschlossen, dass ein oder mehrere Clients einen TimeOut haben, da man ja vorher schon versucht hat, deren Verbindungen zu trennen. Ob noch Einträge in dieser Liste vorhanden sind, wird in einer Boolean-Variable gespeichert. Und am Schluss wird abhängig davon, ob noch Clients verbunden sind, eine Exception ausgelöst, dass eben nicht alle Clientverbindungen getrennt werden konnten, eben durch einen TimeOut der Clients.

So, nachdem ich das jetzt alles getippt hab :stupid: merk ich grade, dass ich nicht sicher bin, ob du eine Erklärung haben wolltest, wie eine Exception durch TimeOuts hier allgemein zustandekommt, oder was an deinem Code falsch ist, dass die Exception kommt :lol:

Aber ich muss noch was hinzufügen^^
In den Prozeduren TTCPIP.StartServer und TTCPIP.ConnectToServer hast du jeweils ein try-try-except-finally Konstrukt, soweit is das auch okay. Aber wenn jetzt ein Fehler auftritt udn der except-Code ausgeführt wird, wird eine Fehlermeldung ausgegeben. Sopäter wird aber immer der finally-Code ausgefürht, welcher meldet, dass alles einwandfrei geklappt hätte. Das wollte ich noch zur Programmlogik schreiben^^

Achja, noch was klitzekleines *g*
Es heißt nicht DisconnectToServer, sondern DisconnectFromServer ;)

Damit ne gute Nacht :hi:

etom291272 16. Nov 2005 21:45

Re: IdTCPServer: RSTerminateThreadTimeout (Terminate Thread
 
Zitat:

Nunja, der Server und Client war zuvor Formlos, d.h. ohne Formular und sollte praktisch "blind" bzw. unsichtbar laufen.
willst du einen dienst schreiben wenn ja kann man die compos schön in die service "form" ziehen
(hab ich schon gemacht läuft wie traktor)

Puhbaehr 16. Nov 2005 22:09

Re: IdTCPServer: RSTerminateThreadTimeout (Terminate Thread
 
Zitat:

Zitat von Ultimator
So, nachdem ich das jetzt alles getippt hab :stupid: merk ich grade, dass ich nicht sicher bin, ob du eine Erklärung haben wolltest, wie eine Exception durch TimeOuts hier allgemein zustandekommt, oder was an deinem Code falsch ist, dass die Exception kommt :lol:

Ja, die Erklärung wollte ich auch haben. Aber auch WARUM ich die Exception bekomme? :) Ok nach deiner Erklärung weil ein Client noch verbunden ist. Aber aber ich trenne doch vorher brav? Ok, dann prüf ich halt gleich mal ob der Client noch verbunden ist.

Zitat:

Aber ich muss noch was hinzufügen^^
...wenn jetzt ein Fehler auftritt udn der except-Code ausgeführt wird, wird eine Fehlermeldung ausgegeben. Sopäter wird aber immer der finally-Code ausgefürht, welcher meldet, dass alles einwandfrei geklappt hätte.
Auf Deutsch ich bekomm bei Fehlern eine negative Meldung und egal ob Fehler oder nicht Fehler immer die gute Nachricht? Ich will ja immer eine gute haben, weil das ist wie Horror wenn es andauernd Fehlermeldungen hagelt... :drunken: nein, Scherz...das hab ich mir aus der Demo von Indy angeschaut. Aber glaube ich falsch umgesetzt. Werd ich ändern.

Zitat:

Es heißt nicht DisconnectToServer, sondern DisconnectFromServer ;)
Ich weiß, klingt jetzt nach Ausrede, aber ich hatte schonmal DisconnectFromServer stehen, ich habs aber wieder umgeschrieben, weil es sah so schön als
ConnectToServer und
DisconnectToClient untereinander stand :)

Zitat:

Damit ne gute Nacht :hi:
Nacht und Danke vorerst!


Zitat:

Zitat von etom291272
Zitat:

Nunja, der Server und Client war zuvor Formlos, d.h. ohne Formular und sollte praktisch "blind" bzw. unsichtbar laufen.
willst du einen dienst schreiben wenn ja kann man die compos schön in die service "form" ziehen

Mh ja nein. Es ist im Prinzip ein Dienst, aber kein Windows-Dienst.
In der Taskleiste soll später ein Icon sein über das sich ein Kontextmenü öffnet in dem man die Konfiguration usw. öffnen kann.
Unteranderem soll darin auch der ClientServer-Dienst rein der später auf den Rechnern im Netzwerk installiert wird.
Die Dienste auf den Rechnern unterhalten sich und erzählen sich z.B. Geschichten ob ein Rechner On/Offline geht oder ob eine Anwendung gestartet wird.
Hat seinen Zweck. Da ich dafür kein Fenster brauch reicht es im Hintergrund. Und ein Application.ShowMainForm := false reicht mir nicht weil ich weiß dass es trotzdem im Speicher existiert. Und da das Programm ständig laufen soll soll es das System sogut wie überhauptnicht belasten.

Zitat:

(hab ich schon gemacht läuft wie traktor)
Traktor ja? Weil er/sie/es nicht aus dem Knick kommt? ;)

Achso, nochetwas:
Ich hab alles eben zum Test in das Form gezogen und den Rest 1 zu 1 übernommen.
Läuft wie... ein Huhn :???: also 1A! Also hab ich doch so wie es aussieht irgendwas beim Create falsch.

DataCool 17. Nov 2005 23:17

Re: IdTCPServer: RSTerminateThreadTimeout (Terminate Thread
 
Hi,

erstmal halte ich Deine Idee, die Indys in ne eigene Klasse auszulagern, nicht unbedingt verkehrt, aber auch nicht zwingend erforderlich !

Habe schon einiges mit den Indys gemacht, teils mit eigenen dynamischen erstellen eigenen Klassen die Indys beinhalten und teils einfach per Drag und Drop der Indy-Komponenten.

Wenn Du den Weg Deiner eigener Klasse weiter gehen möchtest, dann solltest Du es auch richtig machen *lol*
Also wo fang ich an ?

Delphi-Quellcode:
constructor TTCPIP.Create (Owner : TComponent);
begin
  inherited Create (Owner);
  ...                              
  CreateTCPServer(Owner); // hier auch den Besitzer übergeben
  CreateTCPClient(Owner); // hier auch den Besitzer übergeben
end;


procedure TTCPIP.CreateTCPServer(Owner : TComponent);
begin                        
  // Dein Code absoluter Schwachsinn, Du erzeugst in Form1 eine Instanz
  // der Klasse TTCPIP und nennst die TCPConnection
  // in Create Deiner eigenen Klasse verweist über Form1,
  // da genau wieder auf die Klasse die Du gerade am erzeugen bist ????
  // Was soll das ?
  //TCPServer := TIdTCPServer.Create (Form1.TCPConnection);  // ist das Form1.TCPConnection im Create richtig?
  TCPServer := TIdTCPServer.Create(Owner);
  // oder TCPServer := TIdTCPServer.Create(nil); Server brauch nicht zwingend einen Besitzer
  with TCPServer do
   begin
    OnConnect := TCPServerOnConnect;
    ...
    OnListenException := TCPServerOnListenException;
   end;  

  ServerConnected := false;
end;  

procedure TTCPIP.CreateTCPClient(Owner : TComponent);
begin                                  
  // Wieder dasselbe wie beim Server, Du bist noch im Create Deiner eigenen Klasse
  // und das Parent/der Besitzer soll über Form1 die Klasse selber seien ???????????
  //TCPClient := TIdTCPClient.Create (Form1.TCPConnection);
  TCPClient := TIdTCPClient.Create(Owner);
  // oder : TCPClient := TIdTCPClient.Create(nil);
 
  with TCPClient do
   begin
    OnConnected := TCPClientOnConnected;
    ...
    OnStatus := TCPClientOnStatus;
   end;
         
  ClientConnected := false;
end;
In Deinen restlichen proceduren könnte das ein oder andere auch noch optimiert/schöner gelöst werden, aber ich bin schreibfaul und die Art der Lösung hängt auch stark davon ab was Du nachher jetzt damit machen willst.
Warum Du Deine Exception bekommst, kann ich Dir sagen(vermute es, bin zu faul es komplett nach zu proggen).
Beim Beenden des Servers werden alle Threads des Servers beendet, d.h. alle Client-Threads UND der Listener-Thread des Servers(der wird nämlich immer erzeugt), und genau dieser lässt sich bei Dir nicht beenden, deshalb die Exception. Den Grund vermute ich in den oben schon beschrieben Fehler(Parentverbiegung).

Hoffe ich konnte Dir weiter helfen, sonst mußte mir mal ne PN schicken.
Mit den Indys kenn ich mich recht gut aus,

Gruß Data

Puhbaehr 18. Nov 2005 05:01

Re: IdTCPServer: RSTerminateThreadTimeout (Terminate Thread
 
Hey hey, nicht so erdrückend!
Ok, das mit dem Create ist wirklich schwachsinn hab ich auch geschrieben, dass ich mir da unsicher bin da ich nicht recht wusste wem ich das "Kind" zuteilen sollte weil es so und so nicht funktionierte und ich sonst keinen Fehler finden konnte.
Und meinen restlichen Code fand ich gar nicht sooo schlimm. Viele Teile davon hab ich inzwischen wieder abgeändert weil sich einiges davon nicht als praktisch erwies und wirklich unschön zusammengeschustert war. Wenn man schon mehrere male größere Projekte geschrieben hat ist es wohl nicht mehr das große Problem von vornherein ein optimales Programm zu schreiben.
Übung macht den Meister *g*

Zurück zum Thema:
Nach ein paar Versuchen mehr hatte ich Create auf Create (Self) umgeschrieben. Und es lief dann irgendwann. Hab nun deinen Tipp mal angenommen. Scheint auch zu funktionieren.

Gestern hab ich angefangen große Teile des Protokolls zu schreiben und dabei so einige Sachen umgepackt. Zum Beispiel hab ich den Start, die Abarbeitung und den Stop des Clienten in einen neuen Thread gepackt. Damit war die Exception RSTerminateThreadTimeout so wie es aussieht gleichzeitig auch hinfällig.
Vielleicht hatte es damit was zu tun, dass Indy wartete bis Server- und Client-Thread beendet werden, da aber der Client-Teil zuvor ans Form gebunden war und das nicht geschlossen wurde kam halt irgendwann nach dem Timeout die Fehlermeldung. Anders kann ich mir das nicht erklären.

Zitat:

Zitat von DataCool
erstmal halte ich Deine Idee, die Indys in ne eigene Klasse auszulagern, nicht unbedingt verkehrt, aber auch nicht zwingend erforderlich !

Wenn ich aber kein Formular hab in das ich die Komponenten unterbringen kann bin ich doch gewissermaßen gezwungen eine eigene Klasse anzulegen. Oder liege ich da falsch?

Zitat:

Zitat von DataCool
Hoffe ich konnte Dir weiter helfen, ...

Ja, auf jeden Fall!

Zitat:

Mit den Indys kenn ich mich recht gut aus, ...
Das hättest du vielleicht nicht sagen dürfen ;)

Zitat:

Gruß Data
Danke!

Gruß, Robert

OffTopic: Wer ist das da eigentlich auf deinem Avatar?

hardy1234 18. Nov 2005 07:03

Re: IdTCPServer: RSTerminateThreadTimeout (Terminate Thread
 
Hi,

das wird wohl an Deine Konstruktion

for i:= 0 to Thread.Count do

Wenn nix mehr da ist versuchst du weiterhin den 0ten Thread zu schließen. Es empfiehlt sich bei solchen Listen immer die Methode

while Thread.Count <= 0 do begin

i:= Thread.Count -1;
// jetzt schließen
end;

zu benutzen. Dann umgeht man das Problem mit dem Zähler. Bei jedem geschlossenem Thread verkürzt sich nämlich die Liste automatisch, dann läuft man zwangsläufig auf den Hammer.


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