Einzelnen Beitrag anzeigen

spitzley

Registriert seit: 4. Jan 2005
2 Beiträge
 
#3

Re: TClientSocket verbraucht Ports (Fehler 10055)

  Alt 3. Jul 2005, 12:43
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:
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;
Bei Connect-Mißerfolg startet der Error Handler den Timer erneut:

Delphi-Quellcode:
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;
Das ganze funktioniert sowohl bei mißglückten Connects (Client-Verbindungsaufbau,aber Server nicht gefunden) als auch bei Disconnects (Server-Absturz).

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:
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;
Es gibt kein Error-Event, wo ich in diesem Handler auf die ungewollte Trennung reagiern könnte:

Delphi-Quellcode:
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;
Warum gibts da kein Event bei den Clients ?

Mein WorlAround wäre: Die Clients prüfen periodisch per DNS-Resolving, ob die Server IP sich geändert hat.

Delphi-Quellcode:
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;
Das erscheint mir aber aufwendig und unelegant.

Wie sonst können nichtsendende Clients den Kontaktverlust zum Server bemerken ?

Idee ?


Grüße

Kurt Spitzley
  Mit Zitat antworten Zitat