Entwickler-Ecke

Internet / Netzwerk - ClientSocket nach Error neu verbinden?


LuMa86 - Mo 03.09.12 19:21
Titel: ClientSocket nach Error neu verbinden?
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); //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 - 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


LuMa86 - 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 - 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 - 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


LuMa86 - 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:
//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 - 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:

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


LuMa86 - 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:
//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 - 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:

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

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;
cu
Narses