Autor Beitrag
LuMa86
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 76



BeitragVerfasst: 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:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
  repeat
    if (TCPClient.Active) then
      TCPClient.Close;
    delay(1000); //Bisschen Zeit lasse, keine Ahnung warum ich das immer mache :P
    TCPClient.Open;
  until TCPClient.Socket.Connected = true;

  ErrorCode := 0;


Eigentlich mussdoch da nicht mehr rein, oder?

Danke,

Lukas
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10181
Erhaltene Danke: 1254

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Sa 08.09.12 23:25 
Moin und :welcome: in der EE!

user profile iconLuMa86 hat folgendes geschrieben Zum zitierten Posting springen:
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:
:shock: Uah, auf gar keinen Fall eine asynchrone Komponente in ihrem eigenen Ereigniss mit einer Schleife malträtieren! :? Das wird nur Exceptions produzieren, sonst nix. :nixweiss:

Ansatz: Wenn dir die Verbindung wegbricht, dann starte einen Timer, der (nach einer kurzen Wartezeit) eine neue Verbindung versucht. :idea: Genereller Tipp: möglichst schnell die Ereignismethoden der Socket-Komponenten verlassen! :idea:

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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 76



BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 76



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10181
Erhaltene Danke: 1254

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Fr 21.09.12 22:21 
Moin!

user profile iconLuMa86 hat folgendes geschrieben Zum zitierten Posting springen:
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? :nixweiss: :lol:

Wie sollen wir das ohne Code rausfinden? :zwinker:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
LuMa86 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 76



BeitragVerfasst: Sa 22.09.12 20:30 
:)

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
//ErrorEvent startet Timer, dann folgendes (OnTimer alle 10sek):

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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10181
Erhaltene Danke: 1254

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: 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? :lupe:
user profile iconLuMa86 hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
    TCPClient.Socket.Disconnect(30033);					
Normalerweise macht man da ein .Close; hin. :?

user profile iconLuMa86 hat folgendes geschrieben Zum zitierten Posting springen:
//ErrorEvent startet Timer, dann folgendes (OnTimer alle 10sek):
Zunächst mal sind 10sek. viel zu wenig! :shock: 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. :nixweiss:

user profile iconLuMa86 hat folgendes geschrieben Zum zitierten Posting springen:
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. :?

user profile iconLuMa86 hat folgendes geschrieben Zum zitierten Posting springen:
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 76



BeitragVerfasst: 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:

ausblenden 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:
//OnError ruft HandleSocketError auf:
procedure TfrmMain.HandleSocketError;
begin
  try
    TCPClient.Close;
    Delay(5000);
    TCPClient.Socket.Connect;
  except
    TimerList.Events[3].Enabled := TRUE; //Sollte sofortiger reconnect nicht klappen... siehe unten
  end;
end;

//Timer (60 ske.)

procedure TfrmMain.TimerListEvents3Timer(Sender: TObject);
begin
  try
    TCPClient.Close;
    Delay(1000);
    TCPClient.Socket.Connect;
  finally

  end;
end;
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10181
Erhaltene Danke: 1254

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Sa 22.09.12 23:58 
Moin!

user profile iconLuMa86 hat folgendes geschrieben Zum zitierten Posting springen:
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. :idea:

user profile iconLuMa86 hat folgendes geschrieben Zum zitierten Posting springen:
Hier nochmal mein etwas verbesserter Code, für den Anfang:
:shock: 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). :mahn: Umkehrschluss: wenn du ohne Delay() nicht auskommst, hast du einen Konzeptfehler in deiner Zustandssteuerung. :idea:

Kommen wir zum Code: :lupe:
user profile iconLuMa86 hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
//OnError ruft HandleSocketError auf:
procedure TfrmMain.HandleSocketError;
begin
  try
    TCPClient.Close;
    Delay(5000);
    TCPClient.Socket.Connect;
  except
    TimerList.Events[3].Enabled := TRUE; //Sollte sofortiger reconnect nicht klappen... siehe unten
  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).
user profile iconLuMa86 hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
//Timer (60 ske.)
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? :gruebel:
  • 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. :nut: 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:
    ausblenden Delphi-Quelltext
    1:
    2:
    3:
    4:
    (Sender as TTimer).Enabled := FALSE; // den Timer als Deferrer nutzen,
    // nächstes Einschalten im OnError dieses Connect-Versuchs
    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