FAQ DelphiConsultez toutes les FAQ
Nombre d'auteurs : 124, nombre de questions : 934, dernière mise à jour : 23 octobre 2024 Ajouter une question
Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums Delphi et Delphi et bases de données de www.developpez.com et de l'expérience personnelle des auteurs.
Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose soient correctes. Les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous souhaitez y apporter des corrections ou la compléter, contactez un responsable (lien au bas de cette page).
Nous espérons que cette FAQ saura répondre à vos attentes. Nous vous en souhaitons une bonne lecture.
L'équipe Delphi de Developpez.com.
Les composants Indy sont des sockets bloquants, c'est à dire qu'une fonction nécessitant l'envoi ou la réception de données ne rendra pas la main tant que tout le volume d'informations n'aura pas été reçu ou envoyé. Pendant ce temps, les messages Windows ne sont plus traités, ce qui se caractérise essentiellement par l'aspect "gelé" de l'application.
Pour y remédier, il y a plusieurs solutions :
- Travailler avec des applications console qui ne demandent pas d'interaction avec l'utilisateur,
- Poser un TIdAntiFreeze sur la fiche. Ce composant appelle automatiquement "ProcessMessages()" pendant les "timeout" des sockets ce qui permet de traiter les messages et de ne pas figer l'application. Au prix d'un léger (mais imperceptible) ralentissement, vous avez tout les avantages des sockets bloquants sans leurs inconvénients.
- Travailler avec des threads. Indy est conçu pour être threadé : créer un nouveau thread comportant le socket et son code empêchera tout blocage (du moins, le thread principal du programme ne sera pas affecté lorsque le (ou les) thread(s) secondaires seront en phase bloquante).
Notez que seul les clients doivent se préoccuper de problèmes de blocage. Les serveurs eux sont automatiquement threadés.
Il y a plusieurs méthodes de synchronisation qui peuvent (qui doivent) être exécutées dans le serveur, étant donné que chaque requête client s'exécute dans un thread particulier.
Objets et variables privées au thread : aucune méthode de synchronisation ni précautions particulières ne doivent être prises, car ces variables ne sont partagées que par l'instance du thread en elle-même.
Objets publics, fichiers, éléments non graphiques : Si l'accès à ces objets se fait en lecture seule il n'y a pas besoin de protection. Si par exemple un fichier INI doit être lu pour extraire une valeur dans une fonction, cette fonction peut être appelée sans protection particulière depuis le thread serveur. Dès que ces éléments doivent être modifiés, on doit utiliser des Sections Critiques (l'objet TCriticalSection). Cela assure qu'un seul thread à la fois exécute les passages de code accédant à des objets non propres aux threads.
Voilà un exemple ajoutant le nom d'un utilisateur dans une TStringList publique:
1. Déclaration d'une section critique, dans les variables globales de la fiche (on n'oubliera pas d'ajouter SyncObjs dans la clause uses):
Code Delphi : | Sélectionner tout |
1 2 | var
GLock : TCriticalSection; |
Code Delphi : | Sélectionner tout |
1 2 3 4 | initialization GLock := TCriticalSection.Create; finalization GLock.Free; |
3. Dans la fonction, on appelle Acquire pour commencer le blocage et Leave pour l'arrêter :
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | procedure TForm1.AddUser(const AUserName: string); begin GLock.Acquire; try //Code ne devant être exécuté que par un thread à la fois if UserList.IndexOf(AUserName)<0 then UserList.Add(AUserName); finally GLock.Leave; end; end; |
Il y a plusieurs manières de procéder avec Synchronize. La première méthode est de créer des procédures dans la fenêtre principale qui mettent à jour l'interface et de les appeler via la méthode Synchronize() du TIdPeerThread :
Code Delphi : | Sélectionner tout |
1 2 3 4 | procedure TForm1.ClientConnected; begin ListBox1.ItemIndex := ListBox1.Items.Add('Un client s''est connecté !') end; |
Code Delphi : | Sélectionner tout |
1 2 3 4 | procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread); begin AThread.Synchronize(Form1.ClientConnected); //MAJ GUI end; |
La seconde méthode est de créer une classe de synchronisation. Il faudra ensuite créer une instance de cette classe, assigner une valeur à sa variable privée Data , puis appeler 'DoSynchronize' avec un thread Indy et la méthode qui doit être invoquée par Synchronize(), et enfin libérer l'objet.
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | TSyncClass = class protected FData : string; public procedure UpdateVCL; //Procédure sans paramètre qui pourra être synchronisée procedure DoSynchronize(AThread : TIdThread; AMethod : TThreadMethod); end; { TSyncClass } procedure TSyncClass.DoSynchronize(AThread: TIdThread; AMethod: TThreadMethod); begin AThread.Synchronize(AMethod); //synchro. end; procedure TSyncClass.UpdateVCL; begin //Procédure manipulant les éléments graphiques de la VCL Form1.ListBox1.ItemIndex := Form1.ListBox1.Items.Add(FData) end; |
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread); begin with TSyncClass.Create do try FData := Format('Le client %s se connecte...',[AThread.Connection.Socket.Binding.PeerIP]); DoSynchronize(AThread,UpdateVCL); finally Free; end; end; |
Les serveurs Indy basés sur TCP ont à peu près tous le même fonctionnement. Par défaut, un thread d'écoute (Listener Thread) est créé et attend les connexions clientes. Dès qu'un client est accepté, le thread d'écoute crée un nouveau thread qui accueillera la connexion cliente. C'est dans le contexte de ce thread que sont déclenchés tous les événements du serveur. Les événements serveur s'exécutent donc dans des threads différents du thread principal. Une fois le client déconnecté, le thread client est supprimé par le thread d'écoute.
Voici la signature classique d'une fonction membre d'un serveur :
Code Delphi : | Sélectionner tout |
1 2 3 4 | procedure TForm1.IdPOP3Server1Execute(AThread: TIdPeerThread); begin // Code end; |
Cela implique que tout accès à des éléments non-membres du thread devra être fait à l'aide de méthodes de synchronisation. De même, l'accès aux éléments graphiques de la VCL devra se faire absolument en utilisant Synchronize().
Pour diffuser un message à tous ses clients, vous pouvez récupérer la liste de tous les threads clients par la propriété Threads du serveur. LockList() est utilisé pour refuser l'accès à la liste pendant qu'on effectue les opérations.
Dans l'exemple suivant, il s'agit d'un simple WriteLn() envoyant le message fourni en paramètre.
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | procedure TForm1.BroadcastMessage(TheMessage : String); var Count: Integer; List : TList; begin List := tcpServer.Threads.LockList; //vérouiller la liste try for Count := 0 to Pred(List.Count) do //Pour chaque thread try TIdPeerThread(List.Items[Count]).Connection.WriteLn(TheMessage); //Envoie le message except TIdPeerThread(List.Items[Count]).Stop; end; finally tcpServer.Threads.UnlockList; //dévérouiller la liste end; end; |
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.