![]() |
TClientSocket verbraucht Ports (Fehler 10055)
Mein Problem ist folgendes:
(Betriebssystem Win XP prof.) n TCP-Clients sollen einen TCP-Server connecten. Bei einem Verbindungsabriss sollen die Clients einen ReConnect bis zum Erfolg durchführen. Die Reconnects werden pro Client über einen Timer durchgeführt:
Delphi-Quellcode:
Bei jedem Reconnect (alle 3 Sek. pro Client) wird ein neuer Port verwendet und das mündet nach einiger Zeit, wenn alle Ports aufgebraucht sind, in einem Absturz (Fehler 10055).
procedure TNetRouter.OnGateway1ReconnectServerTimer(Sender: TObject);
begin if FGateway1Activate and not(FGateway1Connected) then begin if FGateway1AutoReconnect then begin inc(FGateway1NoOfConnectRetries); Gateway1TCPClientSock.Open; end; FGateway1ReconnectServerTimer.Enabled:=false; (Connect-Error schaltet Timer wieder ein) end; end; Auch anderere Clients "fressen" Ports auf. Bsw. Hyperterminal, Indy-Demo-TCP-Client etc. Das ganze scheint aber ein Win-Problem zu sein. Windows gibt die bereits verwendeten Ports nicht schnell genug wieder frei. So das Ergebnis einer Google-Recherche. Auch die gefundenen Work-Arounds greifen nicht. (MaxUserPorts etc.) Meine Frage also: Kennt jemand eine Lösung für stabile Reconnects für viele TCP-Clients? [edit=alcaeus]Delphi-Tags eingefuegt. In Zukunft bitte selbst machen. Danke Mfg, alcaeus[/edit] |
Re: TClientSocket verbraucht Ports (Fehler 10055)
Hi spitzley,
der Ordnunghalber würd ich beim Reconnecten im Fehlerfall aufräumen statt ständig nur weiter neu auf zu machen. Möglicherweise läuft der Client dann nicht mehr voll. :zwinker: Und das gleiche würde ich bei der eigentlichen, abgebrochenen Verbindung auch mit ner Exception abfangen. Sprich: die alte Verbindung vor dem Reconnect auch zuerst schliessen bevor der Timer angemacht wird. Im Weiteren würd ich den Timer langsam länger takten. Nachdem Du von mehr als einem Client sprichst feuerst Du sonst Deinen Server irgendwann nieder. Zwar ist sich jeder selbst der Wichtigste und will gleich wieder ran aber mit nem toten Pferd reitet es sich im Allgemeinen schlecht.
Delphi-Quellcode:
Have fun, Bernd
procedure TNetRouter.OnGateway1ReconnectServerTimer(Sender: TObject);
begin if FGateway1Activate and not(FGateway1Connected) then begin if FGateway1AutoReconnect then [b]try[/b] inc(FGateway1NoOfConnectRetries); Gateway1TCPClientSock.Open; [u]FGateway1ReconnectServerTimer.Interval:= 3000;[/u] [b]except HandleReConnectError;[/b] end; FGateway1ReconnectServerTimer.Enabled:=false; (Connect-Error schaltet Timer wieder ein) end; end; [b]Procedure TNetRouter.HandleReConnectError; begin Gateway1TCPClientSock.Close; [u]FGateway1ReconnectServerTimer.Interval:= FGateway1ReconnectServerTimer.Interval + 1000;[/u] end;[/b] |
Re: TClientSocket verbraucht Ports (Fehler 10055)
Hallo Bernd !
Danke für Deine Antwort. Ich habe das Problem ähnlich Deinem Vorschlag gelöst: Ein Retry-Counter verlangsamt dynamisch den Reconnect-Timer-Takt:
Delphi-Quellcode:
Bei Connect-Mißerfolg startet der Error Handler den Timer erneut:
procedure TNetRouter.OnGateway1ReconnectServerTimer(Sender: TObject);
begin if FGateway1Activate and not(FGateway1Connected) then begin if FGateway1AutoReconnect then begin inc(FGateway1NoOfConnectRetries); CtrlDlg.NotifyGateway1Info(format('ReConnect %d',[FGateway1NoOfConnectRetries]), $0080FFFF); Gateway1TCPClientSock.Close; Gateway1TCPClientSock.Open; end; FGateway1ReconnectServerTimer.Enabled:=false; FGateway1ReconnectServerTimer.Interval:=FGateway1NoOfConnectRetries*ReconnectServerTimerIntervalFactor; end; end;
Delphi-Quellcode:
Das ganze funktioniert sowohl bei mißglückten Connects (Client-Verbindungsaufbau,aber Server nicht gefunden) als auch bei Disconnects (Server-Absturz).
procedure TNetRouter.Gateway1TCPClientSockError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin ErrorCode := 0; case ErrorEvent of eeGeneral: //Der Socket erhielt eine Fehlermeldung, die in keine der folgenden Kategorien paßt. CtrlDlg.NotifyGateway1Info('ClErr:unbek. Fehler', $0080FFFF); eeSend: //Beim Schreiben der Socket-Verbindung trat ein Fehler auf. CtrlDlg.NotifyGateway1Info('ClErr:Schreibfehler', $0080FFFF); eeReceive: //Beim Lesen der Socket-Verbindung trat ein Fehler auf. CtrlDlg.NotifyGateway1Info('ClErr:Lesefehler', $0080FFFF); TErrorEvent(6),eeConnect: //Bei Client-Sockets bedeutet dieser Wert, daß der Server nicht gefunden //wurde oder daß ein Problem auf dem Server das Öffnen der Verbindung //verhindert. Bei Server-Sockets bedeutet dieser Wert, daß eine //Client-Verbindungsanforderung, die bereits angenommen wurde, //nicht beendet werden kann. begin CtrlDlg.NotifyGateway1Info('ClErr:Keine Verbindung', clRed); if Gateway1AutoReconnect and Gateway1ActivateTCPClient then FGateway1ReconnectServerTimer.Enabled:=true; end; eeDisconnect: begin //Beim Schließen einer Verbindung trat ein Fehler auf. CtrlDlg.NotifyGateway1Info('ClErr:Disconnect', clRed); if Gateway1AutoReconnect and Gateway1ActivateTCPClient then FGateway1ReconnectServerTimer.Enabled:=true; end; end; end; Was ich nicht verstehe, ist Folgendes: Der Server läuft an einem DSL-Anschluss mit dynamischer IP. Ändert sich jetzt die IP nach einigen Stunden, dann gibts es beim Server ein "normales" Disconnect-Event und er trennt sich von seinen Clients. Die Clients kriegen nichts vom IP-Wechsel mit und meinen weiterhin, sie seien verbunden. (Es fließen keine Daten zum Server ! Das würde die Clients den Zusammenbruch merken lassen)
Delphi-Quellcode:
Es gibt kein Error-Event, wo ich in diesem Handler auf die ungewollte Trennung reagiern könnte:
procedure TNetRouter.Gateway1TCPServSockClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
begin try FConnectedTCPClient:=ConnectedTCPClientBySocket(FGateway1ConnectedTCPClientList,Socket); RemoveConnectedTCPClient(FGateway1ConnectedTCPClientList,Socket); CtrlDlg.NotifyGateway1Info('Noch '+IntToStr(FGateway1ConnectedTCPClientList.Count)+' Teilnehmer', $0080FFFF); Log('GW1,TCP-Server,getrennt von '+Socket.RemoteAddress+':'+IntToStr(Socket.RemotePort)); Gateway1Connected:=FGateway1ConnectedTCPClientList.Count<>0; except end; end;
Delphi-Quellcode:
Warum gibts da kein Event bei den Clients ?
procedure TNetRouter.Gateway1TCPServSockClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin ErrorCode := 0; case ErrorEvent of eeGeneral: //Der Socket erhielt eine Fehlermeldung, die in keine der folgenden Kategorien paßt. CtrlDlg.NotifyGateway1Info('SvErr:unbek. Fehler', $0080FFFF); eeSend: //Beim Schreiben der Socket-Verbindung trat ein Fehler auf. CtrlDlg.NotifyGateway1Info('SvErr:Schreibfehler', $0080FFFF); eeReceive: //Beim Lesen der Socket-Verbindung trat ein Fehler auf. CtrlDlg.NotifyGateway1Info('SvErr:Lesefehler', $0080FFFF); eeConnect: //Bei Client-Sockets bedeutet dieser Wert, daß der Server nicht gefunden //wurde oder daß ein Problem auf dem Server das Öffnen der Verbindung //verhindert. Bei Server-Sockets bedeutet dieser Wert, daß eine //Client-Verbindungsanforderung, die bereits angenommen wurde, //nicht beendet werden kann. CtrlDlg.NotifyGateway1Info('SvErr:Verbindungsaufbau', $0080FFFF); eeDisconnect: begin //Beim Schließen einer Verbindung trat ein Fehler auf. RemoveConnectedTCPClient(FGateway1ConnectedTCPClientList,Socket); CtrlDlg.NotifyGateway1Info('SvErr: Noch '+IntToStr(FGateway1ConnectedTCPClientList.Count)+' Teilnehmer', $0080FFFF); end; eeAccept: //Beim Übernehmen einer Client-Verbindungsanforderung trat ein Fehler auf (nur bei Server-Sockets). CtrlDlg.NotifyGateway1Info('SvErr:Verb.anford.', $0080FFFF); end; end; Mein WorlAround wäre: Die Clients prüfen periodisch per DNS-Resolving, ob die Server IP sich geändert hat.
Delphi-Quellcode:
Das erscheint mir aber aufwendig und unelegant.
function GetIPAddressByDNSQuery(URL, DNSServer : string):string;
var IdDNSResolver: TIdDNSResolver; I: Integer; begin Result:=''; IdDNSResolver:=TIdDNSResolver.Create(nil); IdDNSResolver.ReceiveTimeout:=6000; //6 Sekunden bis zum Timeout !!!!! IdDNSResolver.Host := DNSServer; try IdDNSResolver.Active := True; try IdDNSResolver.QueryResult.Clear; IdDNSResolver.QueryRecords := [qtSTAR]; IdDNSResolver.Resolve(URL); if IdDNSResolver.QueryResult.Count<>0 then for I := 0 to IdDNSResolver.QueryResult.Count-1 do begin case IdDNSResolver.QueryResult.Items[I].RecType of qtA: begin Result:=TARecord(IdDNSResolver.QueryResult.Items[I]).IPAddress; break; end; end; end; except end; finally IdDNSResolver.Active := False; IdDNSResolver.Free; end; end; Wie sonst können nichtsendende Clients den Kontaktverlust zum Server bemerken ? Idee ? Grüße Kurt Spitzley |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:27 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