![]() |
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:
Die Klasse TTCPIP stand zuvor allein da und binde ich nun mit:
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; ...
Delphi-Quellcode:
in das Formular ein.
type
TForm1 = class(TForm) ... published TCPConnection : TTCPIP; ... ... var Form1: TForm1; Beim Erstellen des Formulars erstelle ich auch die Komponente:
Delphi-Quellcode:
Im Constructor Create von der TTCPIP-Komponente erstelle ich Server und Client mit:
procedure TForm1.FormCreate(Sender: TObject);
begin TCPConnection := TTCPIP.Create (Application); end;
Delphi-Quellcode:
Auf dem Form hab ich zwei Buttons, einer zum Verbinden, der andere zum Trennen.
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;
Delphi-Quellcode:
Zusätzlich zwei Memos in denen ich im einem Fehlermeldungen ausgeben lasse und im anderen "neutrale" (also auf deutsch: gute) Meldungen.
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; ... Beim Klick auf Button1 wird erst der Server gestartet und Anschließend der Client zum Server verbunden:
Delphi-Quellcode:
Bis dahin ist alles in Ordnung.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; Beim Klick auf den zweiten Button wird der Client vom Server getrennt und der Server beendet:
Delphi-Quellcode:
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:
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;
Delphi-Quellcode:
...mit raise EIdTerminateThreadTimeout.Create(RSTerminateThread Timeout); durch die Exception aus.
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 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 |
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... |
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. |
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? |
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: |
Re: IdTCPServer: RSTerminateThreadTimeout (Terminate Thread
Zitat:
(hab ich schon gemacht läuft wie traktor) |
Re: IdTCPServer: RSTerminateThreadTimeout (Terminate Thread
Zitat:
Zitat:
Zitat:
ConnectToServer und DisconnectToClient untereinander stand :) Zitat:
Zitat:
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:
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. |
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:
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.
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; 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 |
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:
Zitat:
Zitat:
Gruß, Robert OffTopic: Wer ist das da eigentlich auf deinem Avatar? |
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 18:43 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