Autor |
Beitrag |
LuMa86
Beiträge: 76
|
Verfasst: Mo 03.09.12 19:21
Hallo,
so einfach die Frage auch klingt, ich habe schonwieder ein Problem :/ Ich arbeite an einem Client-Server-Projekt. Das ganze realisiere ich mit den Sockets. Einen Anreiz habe ich mir hier aus dem Tut von Nares geholt. Und zwar soll der Client, beim OnError-Event wieder versuchen eine Verbindung herzustellen (falls der Server abstürzt, der Server-PC neu startet, was auch immer...). Allerding bekomme ich nicht hin das umzusetzen.
Eigentlich müsste ich doch alles nur in eine repeat ... until-Schleife packen. Beim Error den ClientSocket schließen (.Close) ErrorCode := 0 setzen und dann die Schleife:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| repeat if (TCPClient.Active) then TCPClient.Close; delay(1000); TCPClient.Open; until TCPClient.Socket.Connected = true;
ErrorCode := 0; |
Eigentlich mussdoch da nicht mehr rein, oder?
Danke,
Lukas
|
|
Narses
Beiträge: 10182
Erhaltene Danke: 1255
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Sa 08.09.12 23:25
Moin und in der EE!
LuMa86 hat folgendes geschrieben : | Eigentlich müsste ich doch alles nur in eine repeat ... until-Schleife packen. Beim Error den ClientSocket schließen (.Close) ErrorCode := 0 setzen und dann die Schleife: | Uah, auf gar keinen Fall eine asynchrone Komponente in ihrem eigenen Ereigniss mit einer Schleife malträtieren! Das wird nur Exceptions produzieren, sonst nix.
Ansatz: Wenn dir die Verbindung wegbricht, dann starte einen Timer, der (nach einer kurzen Wartezeit) eine neue Verbindung versucht. Genereller Tipp: möglichst schnell die Ereignismethoden der Socket-Komponenten verlassen!
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
Für diesen Beitrag haben gedankt: LuMa86
|
|
LuMa86
Beiträge: 76
|
Verfasst: Di 18.09.12 08:54
Danke, hab leider irgendwie deine Antwort verpennt Okay, merke ich mir, dann bastel ich das eben mit einem Timer
|
|
LuMa86
Beiträge: 76
|
Verfasst: Fr 21.09.12 22:01
Ich muss es nochmal pushen, aber ich ahbe immer noch ein Problem: Sollte sich der Client nach einem Error neu verbinden oder vor dem Server gestartet werden (Client starten und versucht zu connecten, dann paar sek. später starte auch der Server), nimmt er keine Kommandos mehr an. Er friert praktisch irgendwo ein. Es gibt am Server auch keinen ClientError. :/ Woran könnte das den jetzt liegen?
|
|
Narses
Beiträge: 10182
Erhaltene Danke: 1255
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Fr 21.09.12 22:21
Moin!
LuMa86 hat folgendes geschrieben : | nimmt er keine Kommandos mehr an. Er friert praktisch irgendwo ein. Es gibt am Server auch keinen ClientError. :/ Woran könnte das den jetzt liegen? |
Der falsche Sack Reis in China umgefallen?
Wie sollen wir das ohne Code rausfinden?
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
LuMa86
Beiträge: 76
|
Verfasst: Sa 22.09.12 20:30
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| procedure TfrmMain.TimerListEvents3Timer(Sender: TObject); begin if (not TCPClient.Socket.Connected) then begin TCPClient.Socket.Disconnect(30033); TCPClient.Close; TCPClient.Host := '*****'; TCPClient.Port := 10011; TCPClient.Open; end else begin TimerList.Events[3].Enabled := False; Exit; end; end; |
Und noch etwas:
Es kommt manchmal vor, wenn ich den Server starte und sich ein Client 2x verbindet, also 2x in der Liste erscheint. Allerdings ist dann nur einer aktiv und der andere reagiert nicht auf Befehle. Und wenn ich die Internetverbindung trenne, dann wird der Clietn auch nicht aus der Liste entfernt, da es kein OnError-Event gibt (bzw. es ausgeführt wird). Dieser Client ist dann zwar da, reagiert aber nciht auf Befehle :/
Und ich ahbe noch eine Interessante entdeckung gemacht: Ich habe eine IdIpWatch, die registriert auch nicht, das ich das Internet abschalte, also die IP wechselt nicht auf 127.0.0.1 sonder bleibt auf meiner lokalen IP im Heimnetzwerk (ich trenne den REchner vom Router!).
|
|
Narses
Beiträge: 10182
Erhaltene Danke: 1255
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Sa 22.09.12 22:46
Moin!
Ich nehme mal an, dass dieser TCPClient in deinem Code vom Typ TClientSocket ist, oder? Kannst du mir mal kurz erklären, was das hier für einen Zweck hat und woher du diese Konstante nimmst?
LuMa86 hat folgendes geschrieben : | Delphi-Quelltext 1:
| TCPClient.Socket.Disconnect(30033); | |
Normalerweise macht man da ein .Close; hin.
LuMa86 hat folgendes geschrieben : | //ErrorEvent startet Timer, dann folgendes (OnTimer alle 10sek): |
Zunächst mal sind 10sek. viel zu wenig! Abgesehen davon war der Tipp mit dem Timer nicht so gemeint, dass du zyklisch einen Disconnect/Connect machst, sondern lediglich als Pause zwischen zwei Versuchen. Wenn du den ClientSocket asynchon mit Ereignissen betreibst (was du offensichtlich tust, da keine try-except-Blöcke da sind), dann musst du auch auf diese Ereignisse reagieren, also einen Connect anstoßen und im entweder eintretenden OnConnect oder (ggfs. nächsten) OnError erst entscheiden, wie weiter zu verfahren ist. Also hier dann den Timer für die Pause starten und in dessen Ereignis wieder den nächsten Connect-Versuch starten. Du wartest ja gar nicht den TCP-Connect-Timeout ab (vermutlich, weil dir das zu lange dauert), aber das solltest du dann anders lösen und keine ereignisgesteuerte Komponente auf diese Weise vergewaltigen. Wenn du unbedingt Kontrolle über die Timeoutspanne haben musst, dann nimm eine andere Komponente oder geh direkt auf die WSA. Wobei dir klar sein sollte, dass das einen Reconnect auch nicht beschleunigt.
LuMa86 hat folgendes geschrieben : | Und noch etwas:
Es kommt manchmal vor, wenn ich den Server starte und sich ein Client 2x verbindet, also 2x in der Liste erscheint. Allerdings ist dann nur einer aktiv und der andere reagiert nicht auf Befehle. Und wenn ich die Internetverbindung trenne, dann wird der Clietn auch nicht aus der Liste entfernt, da es kein OnError-Event gibt (bzw. es ausgeführt wird). Dieser Client ist dann zwar da, reagiert aber nciht auf Befehle :/ |
Das ist ein anderes Problem, dass wir in diesem Thread bitte nicht weiter verfolgen wollen, sonst gibt das Chaos. Abgesehen davon wüsste ich dazu eh nichts beizutragen, weil das auf Wahrsagen rausläuft.
LuMa86 hat folgendes geschrieben : | Und ich ahbe noch eine Interessante entdeckung gemacht: Ich habe eine IdIpWatch, die registriert auch nicht, das ich das Internet abschalte, also die IP wechselt nicht auf 127.0.0.1 sonder bleibt auf meiner lokalen IP im Heimnetzwerk (ich trenne den REchner vom Router!). |
Ich benutze die Indies nicht, wenn ich das vermeiden kann. Den IdIpWatch kenne ich nur dem Namen nach, habe ich noch nie benutzt, ich könnte mir aber vorstellen, dass die da auch nicht permanent die WSA pollen, sondern cachen. Vielleicht gibt´s da ja eine Update-Methode oder so. Aber auch hier: Das ist eine neue Frage, die gehört bitte in ein neues Thema.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
LuMa86
Beiträge: 76
|
Verfasst: Sa 22.09.12 23:14
Okay, ich bastel das alles Schritt für Schritt nochmal um, danke dir Aber eine kleine Sache noch: Was ist der UNterschied zw. "TCPClient.Socket.Close;" und "TCPClient.Close;"?
Hier nochmal mein etwas verbesserter Code, für den Anfang:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| procedure TfrmMain.HandleSocketError; begin try TCPClient.Close; Delay(5000); TCPClient.Socket.Connect; except TimerList.Events[3].Enabled := TRUE; end; end;
procedure TfrmMain.TimerListEvents3Timer(Sender: TObject); begin try TCPClient.Close; Delay(1000); TCPClient.Socket.Connect; finally
end; end; |
|
|
Narses
Beiträge: 10182
Erhaltene Danke: 1255
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Sa 22.09.12 23:58
Moin!
LuMa86 hat folgendes geschrieben : | Aber eine kleine Sache noch: Was ist der UNterschied zw. "TCPClient.Socket.Close;" und "TCPClient.Close;"? |
Vom Ergebnis bzgl. der Verbindung her keiner, die ist anschließend zu. Aber intern ist bei TClientSocket.Close; der WSA-Socket noch gebunden, bei TClientSocket.Socket.Close; wird auch der WSA-Socket weggeräumt. Fazit: Wenn du absehbar noch weitere Verbindungen aufbauen willst, sollte man den gebunden lassen. Sonst zwingt man der WSA unnötig Arbeit auf. Aber: nach einem Fehler in der Verbindung sollte man sicherheitshalber auch einen neuen WSA-Socket erzeugen lassen.
LuMa86 hat folgendes geschrieben : | Hier nochmal mein etwas verbesserter Code, für den Anfang: |
Vergiss am Besten gleich wieder, dass es Delay() gibt (es sei denn, du arbeitest in einem Thread)! Das hat in ereignisorientierten Anwendungen nix zu suchen (ja, wirklich nix, ganz ohne Ausnahme). Umkehrschluss: wenn du ohne Delay() nicht auskommst, hast du einen Konzeptfehler in deiner Zustandssteuerung.
Kommen wir zum Code:
LuMa86 hat folgendes geschrieben : | Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure TfrmMain.HandleSocketError; begin try TCPClient.Close; Delay(5000); TCPClient.Socket.Connect; except TimerList.Events[3].Enabled := TRUE; end; end; | |
- Was soll das try-except bewirken? Hast du in den blocking-mode des TClientSocket geschaltet? Wenn nicht ist das Tulux und wird eh nie im except-Zweig landen.
- Du schreibst, dass HandleSocketError aus OnError des Sockets kommt, also faktisch im gleichen Ereignis behandelt wird. Dann möchte ich a) an den Tipp mit dem möglichst schnellen Verlassen von Ereignishandlern erinnern (wie du überhaupt auf Delay() und einen Timer gekommen bist, ist mir eh rätselhaft) und b) solltest du keine Reconnects in Socket-Ereignissen einleiten, dass (kann) Ärger mit der MessageLoop geben! Deshalb der Tipp mit dem Timer, weil in dessen Ereignis bist du dann nämlich aus dem Socket-Ereignis wieder raus. Alternativ kannst du das auch mit einer Selbstbenachrichtigung lösen (eigene Message definieren und sich selbst posten).
LuMa86 hat folgendes geschrieben : | Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure TfrmMain.TimerListEvents3Timer(Sender: TObject); begin try TCPClient.Close; Delay(1000); TCPClient.Socket.Connect; finally
end; end; | |
- Was soll das try-finally bewirken? Noch dazu mit leerem finally-Zweig?
- Auch hier wieder: Du machst ein Close, frierst die Anwendung dann ein (gibst ihr also keine Gelegenheit dieses .Close; korrekt mit Ereignissen zu behandeln) und machst dann direkt einen Connect. Das ist vom Stil her keine ereignisorientierte Programmierung, sondern blocking-sockets mit Threads! Du musst ereignisorientiert denken: wenn du eine Aktion wie z.B. .Connect; startest, dann geht´s erst wieder in einem der Folgeereignisse weiter, also in einem OnConnect oder im OnError. Dito auch bei .Close; hier geht´s dann wieder im OnDisconnect oder OnError weiter.
- Eigentlich gehört in das Timer-Ereignis nur das hier rein:
Delphi-Quelltext 1: 2: 3: 4:
| (Sender as TTimer).Enabled := FALSE; if NOT ClientSocket1.Active then ClientSocket1.Open; | Alles andere muss in den anderen Ereignissen passieren.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
Für diesen Beitrag haben gedankt: LuMa86
|
|
|