IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Web & réseau Delphi Discussion :

Intercepter la déconnexion du serveur


Sujet :

Web & réseau Delphi

  1. #1
    Membre régulier
    Homme Profil pro
    Inscrit en
    Juin 2012
    Messages
    142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations forums :
    Inscription : Juin 2012
    Messages : 142
    Points : 80
    Points
    80
    Par défaut Intercepter la déconnexion du serveur
    Bonjour

    Je viens de m’apercevoir sur une application Client est Serveur TCP (IdTCPClient & IdTCPServer) que si le serveur se déconnecte brutalement le client ne peut plus se reconnecter au serveur, si je relance une application client je peux me connecter au serveur, mais tous les clients connectés sur le serveur lors de sa déconnexion brutale ne peuvent plus se reconnecter sur le serveur.

    Le problème vient du IdTCPClient j'ai fait un teste avec Create (IdTCPClient) dynamiquement en procédure de connexion puis Free pour sa déconnexion, est la il est possible de se reconnecter au serveur.

    Avez-vous une idée d’où peut venir le problème ?

    Merci.
    Il n'existe guère de problèmes sans solution, et parfois l'absence de solution décourage le problème

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Je n'utilise pas les composants Indy, je leur préfère TClientSocket et TServerSocket, que ce soit en D5, D7, BCB2007, BCBXE3, DXE2, je n'ai jamais eu aucun soucis, que ce soit en mode Not Blocking qu'en mode Thread Blocking
    Longtemps méprisé, ils ont été réhabilités avec l'abandon de TTCPClient et TTCPServer en XE6 et sont utilisé comme couche TCP de base en interne des classes Delphi de DataSnap !
    Si Embarcadero les utilises dans un produit phare comme DataSnap, c'est qu'ils sont fiables !

    Mais, c'est vrai que la déconnexion brutale du serveur, c'est un point faible des composants TCP
    J'ai un programme de Test et le programme Serveur, il m'arrive souvent de faire un CTRL+F2 du serveur, le client se reconnecte sans problème (le client lorsqu'il détecte une erreur, fait un Close explicite de la connexion de son côté puis se reconnecte)


    Sinon, avec les Indy, c'est toujours un problème de Binding
    J'ai mis en place TIdFTPServer et au départ, le lancement du server et de plusieurs client sur le même poste provoquait une erreur "port déjà utilisé"

    j'ai forcé le Bindings, et c'est nettement plus stable !
    TIdFTPServer est basé sur le TIdTCPServer dont il hérite Bindings !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
          // Ouverture (Ecoute des connexions)
          if FFTPServer.Bindings.Count = 0 then
          begin
            with FFTPServer.Bindings.Add() do
            begin
              // Ajouter le Bindings en 0.0.0.0 refuse la Connexion en "[::1]:21..."
              // Cela provoque sous FileZilla : Échec de la tentative de connexion avec "ECONNREFUSED - Connection refused by server", essai de l'adresse suivante.
              // Cela utilise ensuite l'IP réelle ou l'IP 127.0.0.1 ce qui évite une erreur sur la commande EPSV : "501 Impossible de lier le socket. L'adresse et le port sont déjà en cours d'utilisation."
              // D'ailleurs, cela change le choix de FileZilla dans ces commandes car il execute plutôt un PASV (Passive Mode) et non un EPSV (Extended Passive Mode)
              // Si le serveur et le client sont sur le même poste, sans le Bindings, je ne peux ouvrir qu'un client Indy mais pas client FileZilla
              // Sur FileZilla, cela se produit lors de la commande EPSV ce qui impacte la commande EPRT
              // Sur InternetExplorer, cela ne se connecte pas du tout
              // Si les clients (même plusieurs) sont lancés depuis un autre poste, il n'y a pas de problème !
              // Avec un client Delphi Indy, je peux sans problème me connecter et envoyer un fichier même sans Bindings !
              // Par précaution, je force une IP explicite
              IP := '0.0.0.0';
              Port := 21;
            end;
          end;
     
          FFTPServer.Active := True;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  3. #3
    Membre régulier
    Homme Profil pro
    Inscrit en
    Juin 2012
    Messages
    142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations forums :
    Inscription : Juin 2012
    Messages : 142
    Points : 80
    Points
    80
    Par défaut Message d'erreur
    J'ai retiré le "try except" lors de la reconnexion du client vers le serveur qui lui est reconnecté, j’obtiens un message d'erreur "socket error #10054 connection reset by peer"
    Il n'existe guère de problèmes sans solution, et parfois l'absence de solution décourage le problème

  4. #4
    Membre régulier
    Homme Profil pro
    Inscrit en
    Juin 2012
    Messages
    142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations forums :
    Inscription : Juin 2012
    Messages : 142
    Points : 80
    Points
    80
    Par défaut TClientSocket et TServerSocket
    Tout compte fait je vais repartir sur les bon vieux composants TClientSocket et TServerSocket qui me paraisse bon plus stable que les "IdTCPServer , IdTCPClient" qui son vraiment galéré .

    Quel est la stabilité d'un TServerSocket entre le "ctNonBlocking , stThreadBlocking" , par exemple si il y a 30 ~ 40 Clients connecté sur le serveur est que chaque clients envoie un message chaque seconde il a t'il un risque de le serveur bloque ?


    Merci pour votre réponse.
    Il n'existe guère de problèmes sans solution, et parfois l'absence de solution décourage le problème

  5. #5
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    J'ai au plus 6 clients simultanés mais c'est finalement le superviseur réseau qui crée le plus d'activité en se connectant toutes les 30 secondes pour savoir si le Port TCP est disponible, au bout de 4 échecs, cela génére un mail d'alerte d'un service Shutdown

    Je l'ai testé massivement ma classe TSLTRemoteMessenger encapsulé métier par TxxxModuleAutomateLogistique avec 3 clients qui envoie un message le serveur ajoute un DateTime puis le renvoie et ainsi de suite
    Cela faisait presque 1000 messages par seconde pour chaque client, je te confirme que le serveur au fur et à mesure prenait un peu de retard pour répondre mais finissait par tout renvoyer
    Tu noteras que dans TSLTRemoteMessenger je gère un thread qui centralise dans une seule file tous les messages reçus depuis les threads des clients
    L'avantage, cela ne bloque pas le thread de lecture pendant le traitement dans mon exemple c'est rapide dans le vrai module c'est des traitements DB de quelques ms à plusieurs secondes.
    Si tu ne lit pas assez souvent le Stream du client, tu peux saturer le tampon TCP/IP de Windows et perdre des données ...
    Inconvénient, cela fait un goulot d'étranglement qui fait que les traitements suivants attendent chacun leur tour, dans mon vrai module, c'est plus prudent et tout fonctionne de façon asynchrone .

    Voici mon petit code de test

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //------------------------------------------------------------------------------
    procedure TAutomateLogistiqueGUI_DevForm.btnMessengerTestClick(Sender: TObject);
    var
      S: string;
      I: Integer;
    begin
      for I := 1 to StrToIntDef(edMessengerTestCount.Text, 0) do
      begin
        S := IntToStr(I) + ' : Salut en ce ' + FormatDateTime('dddd dd mmmm yyyy à hh:nn:ss-zzz', Now());
        if TxxxModuleAutomateLogistique.RemoteMessageSystem(rmstKeepAlive, PChar(S), Length(S) * SizeOf(Char)) then
          MemoMessengerTest.Lines.Add(S)
        else
          MemoMessengerTest.Lines.Add('oups');
      end;
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
     
     
    Même code côté serveur que client pour retourner la réponse
     
    //------------------------------------------------------------------------------
    procedure TAutomateLogistiqueGUI_DevForm.RemoteMessageNotifyEventHandler(AOrigin: TxxxRemoteMessageSocketHandle; const ARemoteMessage: TxxxModuleAutomateLogistique.TRemoteMessage);
    var
      SynchronizedRemoteMessage: TxxxModuleAutomateLogistique.TRemoteMessage;
    begin
      SynchronizedRemoteMessage := ARemoteMessage;
     
      TrySynchronize(
        procedure
        var
          S: string;
        begin
          MemoMessengerTest.Lines.Add(System.TypInfo.GetEnumName(TypeInfo(TxxxModuleAutomateLogistique.TRemoteMessageType), Ord(SynchronizedRemoteMessage.MessageType)));
          if SynchronizedRemoteMessage.MessageType = rmtSystem then
          begin
            if (SynchronizedRemoteMessage.System.SystemType = rmstKeepAlive) and (SynchronizedRemoteMessage.System.SystemDataLen = SizeOf(TxxxModuleAutomateLogistique.TRemoteMessageSystemKeepAlive)) then
            begin
              S := 'Keep Alive @ ' + FormatDateTime('dddd dd mmm yyyy à hh:nn:ss', TxxxModuleAutomateLogistique.TRemoteMessageSystemKeepAlive(SynchronizedRemoteMessage.System.SystemData^).TimeStamp);
              MemoMessengerTest.Lines.Add(S);
            end
            else
            begin
              SetLength(S, SynchronizedRemoteMessage.System.SystemDataLen div SizeOf(Char));
              CopyMemory(PChar(S), SynchronizedRemoteMessage.System.SystemData, SynchronizedRemoteMessage.System.SystemDataLen);
              MemoMessengerTest.Lines.Add(S);
            end;
     
            if chkMessengerTestResponse.Checked then
            begin
              S := 'Au revoir du ' + S;
              if Length(S) <= 200 then
                TxxxModuleAutomateLogistique.RemoteMessageSystem(rmstKeepAlive, PChar(S), Length(S) * SizeOf(Char), AOrigin)
              else
                MemoMessengerTest.Lines.Add('Cycle terminé');
            end;
          end;
        end
      );
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  6. #6
    Membre régulier
    Homme Profil pro
    Inscrit en
    Juin 2012
    Messages
    142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations forums :
    Inscription : Juin 2012
    Messages : 142
    Points : 80
    Points
    80
    Par défaut IdTCPClient
    J'ai trouvé pourquoi le client ne peut pas se reconnecter au serveur lorsque le serveur se déconnecté brutalement pour X cause , perte de connexion internet ,ect ..., dans la partie client "Déconnexion" j'ai rajouté cette ligne "IdTCPClient. Socket. Close" , est le client peu se reconnecter au serveur sans problème.
    Il n'existe guère de problèmes sans solution, et parfois l'absence de solution décourage le problème

  7. #7
    Membre régulier
    Homme Profil pro
    Inscrit en
    Juin 2012
    Messages
    142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations forums :
    Inscription : Juin 2012
    Messages : 142
    Points : 80
    Points
    80
    Par défaut
    Pour se qui est de la déconnexion du serveur brutal , Coté client j'ai plus m'apercevoir que si le serveur est déconnecté le client envoie une expression "connection reset by peer" donc avec le composant TIdThreadComponent on peu tester la connexion

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    IdTCPClient.Connect;
    IdThreadComponent.Active := True;
     
    IdThreadComponent.OnRun 'On teste a connexion'
     
    try
      if not IdTCPClient.Connected then
       begin {Le client se déconnecté manuellement}    
         IdThreadComponent.Terminate; 
         ShowMessage('Client Disconnect');  
       end;
     
      except {Le serveur c'est déconnecter}
        IdThreadComponent.Terminate;
        ShowMessage('Server Disconnect');
      end;
     
    IdThreadComponent.OnTerminate
     
    IdTCPClient.Disconnect;
    ...
    IdThreadComponent.Active := False;
    Après il a peu être d'autre solution ?
    Il n'existe guère de problèmes sans solution, et parfois l'absence de solution décourage le problème

  8. #8
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Citation Envoyé par Gregory666 Voir le message
    J'ai trouvé pourquoi le client ne peut pas se reconnecter au serveur lorsque le serveur se déconnecté brutalement pour X cause , perte de connexion internet ,ect ..., dans la partie client "Déconnexion" j'ai rajouté cette ligne "IdTCPClient. Socket. Close" , est le client peu se reconnecter au serveur sans problème.
    Citation Envoyé par ShaiLeTroll Voir le message
    J'ai un programme de Test et le programme Serveur, il m'arrive souvent de faire un CTRL+F2 du serveur, le client se reconnecte sans problème (le client lorsqu'il détecte une erreur, fait un Close explicite de la connexion de son côté puis se reconnecte)
    Au final, que ce soit Indy ou Natif, il faut en cas de déconnexion brutale fait un close explicite du côté client comme je l'avais soupçonné

    Citation Envoyé par Gregory666 Voir le message
    Après il a peu être d'autre solution ?
    Je ne peux pas répondre concernant Indy

    le composant TClientSocket, il n'a pas toujours le même comportement lors d'une déconnexion,
    le WaitForData renvoie True si déconnecté et c'est le Read qui renvoie juste 0 et parfois même déclenche une exception d'erreur de lecture de flux.
    Dans ce cas, je stoppe la connexion

    voici mon thread de lecture (aussi bien serveur lisant chaque client que le client lisant le serveur)

    Tu notes que le mode thread est finalement très basique, au moindre problème, je ferme le connexion
    En plus par dessus, j'ai ajouté un Keep Alive maison (sans passer par setsockopt \ SO_KEEPALIVE \ SIO_KEEPALIVE_VALS) pour garantir que la connexion est stable et les threads ne sont pas gelés

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    //------------------------------------------------------------------------------
    procedure TSLTRemoteMessengerClientReader.Process();
    var
      ReadCount: Integer;
    begin
      // WaitForData return on
      // Data is available for reading
      // Connection has been closed/reset/terminated.
      if WaitForData(FWaitDelay) then
      begin
        if FSocket.Connected and (FSocket.SocketHandle > 0) then
          ReadCount := Read(FBuffer[0], BUFFER_LEN)
        else
          ReadCount := 0;
     
        if ReadCount > 0 then
          ScanBuffer(FSocket.SocketHandle, @FBuffer[0], ReadCount)
        else
          FSocket.Close(); // Connexion trop lente ou déconnectée !
      end
      else
        if FSocket.Connected then
          DoIdle();
    end;
     
     
    //------------------------------------------------------------------------------
    procedure TSLTRemoteMessengerClientThread.Execute();
    var
      Stream: TSLTRemoteMessengerClientReader;
    begin
      try
        Stream := TSLTRemoteMessengerClientReader.Create(FMessenger, FSocket, READ_DELAY);
        try
          while not Terminated and FSocket.Connected do
            Stream.Process();
        finally
          Stream.Free();
        end;
      except
        on E: Exception do
        begin
          {$IFDEF DEBUG_SLT_TCP}OutputDebugTCP('ClientThread : ' + E.Message);{$ENDIF DEBUG_SLT_TCP}
          FSocket.Close();
        end;
      end;
    end; 
     
    //------------------------------------------------------------------------------
    procedure TSLTRemoteMessengerServerClientThread.Execute();
    var
      Stream: TSLTRemoteMessengerClientReader;
    begin
      try
        Stream := TSLTRemoteMessengerClientReader.Create(FMessenger, ClientSocket, KEEP_ALIVE_TIME_OUT);
        try
          while not Terminated and ClientSocket.Connected do
            Stream.Process();
        finally
          Stream.Free();
        end;
      except
        on E: Exception do
        begin
          {$IFDEF DEBUG_SLT_TCP}OutputDebugTCP('ServerClientThread : ' + E.Message);{$ENDIF DEBUG_SLT_TCP}
          ClientSocket.Close();
        end;
      end;
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [VBA] Erreur 52 lorsqu'il y a déconnexion au serveur
    Par banzaii dans le forum Général VBA
    Réponses: 6
    Dernier message: 27/06/2017, 22h49
  2. Déconnexion sur serveurs multiples
    Par Miaou le chat thon dans le forum Langage
    Réponses: 1
    Dernier message: 31/08/2010, 12h03
  3. Problème déconnexion du serveur
    Par sarah_ dans le forum Windows Serveur
    Réponses: 2
    Dernier message: 24/02/2010, 10h56
  4. Déconnexion à un serveur du réseau
    Par Piet dans le forum Windows XP
    Réponses: 9
    Dernier message: 17/01/2008, 09h44

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo