FAQ DelphiConsultez toutes les FAQ
Nombre d'auteurs : 124, nombre de questions : 933, dernière mise à jour : 28 septembre 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.
- Comment dois-je faire pour répondre à un client avec mon serveur ?
- Comment échanger du texte entre client et serveur ?
- Comment compresser les flux entre client et serveur ?
- Comment envoyer des enregistrements (records) entre un client et un serveur ?
- Comment transférer un fichier entre un client et un serveur ?
- Comment stocker des informations sur un client ?
- Comment utiliser le protocole UDP avec Delphi ?
Si vous utilisez un serveur basé sur un protocole pris en charge par Indy (HTTP, FTP, SMTP, etc.), celui-ci fournit des événements dans lesquels vous avez ou vous pouvez, directement ou indirectement, communiquer avec le client. La plupart du temps vous n'avez pas besoin d'envoyer directement du texte ou des commandes "brutes", il s'agit de remplir des propriétés ou des variables que Indy utilisera pour générer une réponse correctement formatée, en fonction du protocole utilisé.
Vous pouvez également vous servir des "Command Handlers". Ces propriétés serveur permettent de réagir à des commandes et d'exécuter des fonctions à leur réception.
Enfin, la dernière solution est d'utiliser l'événement OnExecute. Celui-ci est déclenché juste après la connexion du client et est appelé en boucle tant que celui-ci n'est pas déconnecté. Vous pouvez dans cet événement écrire directement sur la connexion et échanger des données avec le client.
Il est important dans cette procédure de bien gérer les exceptions, car en cas d'erreur non gérée, l'exécution de la procédure sera interrompue, et la connexion avec le client laissée en suspens tant que la pile TCP/IP ne ferme pas la connexion pour cause de "time out". Cela pouvant prendre une ou deux minutes, on utilisera toujours un bloc try...finally...end.
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread); begin with AThread.Connection do try try //Travail à effectuer except //Notification d'une erreur au client on E : Exception do WriteLn(Format('ERROR : %s',[e.Message])); end; finally Disconnect; //Fermeture de la connexion end; end; |
Si vous souhaitez échanger facilement des lignes de texte entre client et serveur, utilisez les fonctions WriteLn() et ReadLn() du TIdTCPClient et de l'objet Connexiondu TIdPeerThread du serveur.
Voici un exemple simple : un serveur qui exécute la ligne de commande qui lui est envoyée. Dans l'extrait de code ci-dessous, le client fait exécuter au serveur la commande "shutdown", et donc commande à distance son extinction.
Code du client
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 | procedure ShutdownServer(const AHost : string; const APort : integer); begin with IdTCPClient do begin Port := APort; Host := AHost; try Connect; //connecte Try IdTCPClient.WriteLn('shutdown -h -t 00'); //envoie la ligne de commande if IdTCPClient.ReadLn()<>'OK' then //attend la réponse. Si <> de OK, erreur : MessageDlg('Erreur : l''exécution a été refusée par le serveur.', mtInformation, [mbOK], 0); finally Disconnect; //dans tous les cas, se déconnecter end; except MessageDlg('Une erreur est survenue durant l''envoi de commandes', mtError, [mbOK], 0); end; end; end; |
Le code se place dans l'événement OnExecute du serveur :
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | procedure TForm1.IdTCPServerExecute(AThread: TIdPeerThread); var Line, Command, Args : String; begin Line := AThread.Connection.ReadLn(); //Lire une ligne AThread.Connection.WriteLn('OK'); //Signifie que la demande a été correctement reçue. //On pourrait renvoyer autre chose pour notifier un refus d'exécution. if Pos(' ',Line)>0 then begin //Il y a un/des paramètre(s), il faut extraire Commande et Arguments Command := Copy(Line,1,Pred(Pos(' ',Line))); Args := Copy(Line,Succ(Pos(' ',Line)),Length(Line)) end else begin //Aucun paramètre. Commande = toute la ligne reçue Command := Line; Args := ''; end; //Exécuter la ligne de commande ShellExecute(Handle,nil,PChar(Command),PChar(Args),nil,SW_SHOWNORMAL); end; |
Il est très facile de compresser les flux échangés entre un client et un serveur descendant respectivement de TIdTCPClient et TIdTCPServer.
Côté client, il faut associer un TIdCompressionIntercept à la propriété Intercept du client. Vous devez entrer un nombre entre 1 et 9 dans la propriété CompressionLevel pour indiquer le niveau de compression à utiliser (0 correspond à une compression nulle).
Côté serveur, vous devez créer dynamiquement pour chaque thread client un CompressionIntercept afin de permettre la décompression du flux de connexion (et le libérer la connexion terminée).
Ceci peut s'implémenter ainsi :
Dans l'évènement OnConnect :
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 | procedure TForm1.IdTCPServerConnect(AThread: TIdPeerThread); begin with (AThread.Connection) do begin Intercept := TIdCompressionIntercept.Create(nil); // Créer le "compresseur" TIdCompressionIntercept(Intercept).CompressionLevel := 9; // Niveau de compression end; end; |
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | procedure TForm1.IdTCPServerDisconnect(AThread: TIdPeerThread); begin with (AThread.Connection) do begin if Assigned(Intercept) then // Assigné ? begin Intercept.Free; // On libère... Intercept := nil; end; end; end; |
Tout d'abord, il faut définir un record :
- dont les membres sont de longueur statique, pour que la taille globale du record soit prévisible du côté client comme du côté serveur. Par conséquent, vous ne pouvez pas utiliser des types de longueur variable automatiquement gérés par Delphi. Le type brut string est à proscrire. Indiquez toujours une longueur maximum (par exemple string [15], pour créer une chaîne de 15 caractères maximum), voire passez vos chaînes par des array of char.
- exempts de tout pointeurs. Les pointeurs désignent un espace mémoire propre à un processus. Le serveur et le client sont généralement sur des machines différentes et ne partagent pas le même processus mémoire : l'adresse transmise ne correspondrait donc à rien.
Exemple d'enregistrement valide :
Code Delphi : | Sélectionner tout |
1 2 3 4 5 | TClient = record Nom : string[50]; Adresse : string[200]; CodeVille : integer; end; |
Code côté client :
On utilisera WriteBuffer pour écrire dans le tampon de connexion. Le dernier paramètre, mis à true, permet de le vider instantanément dans le flot pour qu'il soit directement transmis au serveur :
Code Delphi : | Sélectionner tout |
IdTCPClient.WriteBuffer(ARecord,sizeof(ARecord),true);
Dans l'événement OnExecute, on utilisera ReadBuffer pour lire dans le tampon :
Code Delphi : | Sélectionner tout |
AThread.Connection.ReadBuffer(Recv,Sizeof(Recv));
On peut facilement envoyer un fichier entre un client et un serveur TCP. Pour cela, on utilise chez le client la méthode WriteStream. Sur le serveur, on se contente de lire le flux et de le placer dans un TFileStream.
Il est nécessaire de mettre au point un minuscule protocole pour que les entités puissent communiquer aisément. Voilà celui que j'ai choisi :
- Le client se connecte au serveur, et lui envoie la commande "TRANS" suivie du nom de fichier qu'il envoie.
- Le serveur reçoit la commande TRANS, et comprend qu'on lui envoie un fichier. Il extrait alors le nom du fichier et crée un TFileStream sur un document du même nom dans son répertoire.
- Le client envoie ensuite la taille du fichier. Cette taille sera nécessaire lorsqu'il faudra lire un flux sur la connexion (spécifier la taille du flux est obligatoire, ou alors le flux est lu jusqu'à la déconnexion, ce qui n'est pas ce que nous souhaitons faire ici).
- Le serveur reçoit la taille du fichier. Il attend donc un flux et le stocke dans le TFileStream jusqu'à que le nombre d'octets reçus atteigne la valeur lue précédemment.
- Pendant ce temps, le client ouvre son tampon d'écriture, place le contenu du fichier à transférer en utilisant WriteStream puis ferme ce flux.
- Une fois la réception terminée, le serveur confirme la bonne réception en écrivant sur la connexion "OK". Notez que toute exception éventellement levée côté serveur fait écrire "ERR" au lieu de "OK".
- Le client attend une réponse du serveur. Si celle-ci est égale à "OK", on en déduit que tout s'est bien passé, et on fait renvoyer true à la fonction, false sinon.
- L'échange terminé, client et serveur se déconnectent mutuellement.
Code de la fonction d'envoi :
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 23 24 25 | function TForm1.SendFile(const AFileName: string; ATcpClient: TIdTCPClient) : Boolean; var Fs : TFileStream; begin Result := False; ATcpClient.Connect(); //Connecte. Les propriétés Host et Port doivent être remplies. try Fs := TFileStream.Create(AFileName,fmOpenRead,fmShareDenyWrite); //Créer le flux try ATcpClient.WriteLn(Format('TRANS %s',[ExtractFileName(AFileName)])); //demander transfert try ATcpClient.WriteInteger(Fs.Size); //Ecrire la taille ATcpClient.WriteStream(Fs); //Ecrit le flux except MessageDlg('Erreur pendant l''envoi du fichier.', mtError, [mbOK], 0); end; finally FreeAndNil(Fs); //Libérer le flux Result := ATcpClient.ReadLn()='OK'; //OK uniquement si le serveur a renvoyé "OK" end; finally ATcpClient.Disconnect; //Déconnecter à la fin. end; end; |
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 23 24 25 26 27 28 29 30 31 32 33 | procedure TForm1.IdTCPServerExecute(AThread: TIdPeerThread); var Line, FileName : String; i, FileSize : integer; Fs : TFileStream; begin with AThread.Connection do try Line := ReadLn(); //Attends une commande de la forme TRANS suivi du nom de fichier i := Pos(' ',Line); if (i>0) and (LowerCase(Copy(Line,1,Pred(i)))='trans') then begin FileName := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName)) + Copy(Line,Succ(i),Length(Line)); //Copier nom de fichier Fs := TFileStream.Create(FileName,fmCreate); //Créer le flux try try FileSize := ReadInteger(); //Lire la taille ReadStream(Fs,FileSize,False); //Lire le flux WriteLn('OK'); //Signaler succès except WriteLn('ERR'); //Signaler une erreur end; finally FreeAndNil(Fs); //Libérer le flux dans tous les cas end; end else WriteLn('ERR'); //Commande incomprise finally Disconnect; //A la fin, on déconnecte end; end; |
Exemple d'exploitation :
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | procedure TForm1.Button1Click(Sender: TObject); begin IdTCPClient.Host := 'localhost'; //Hôte IdTCPClient.Port := 1985; //Le serveur doit aussi écouter sur le port 1985 if SendFile('c:\setup.exe',IdTCPClient) then MessageDlg('Ok !', mtInformation, [mbOK], 0) else MessageDlg('Erreur...', mtInformation, [mbOK], 0) end; |
Dans les protocoles à connexions "persistantes" (la connexion est maintenue durant toute la session), on peut utiliser dans le thread associé au client la propriété Data de type TObject pour stocker un ensemble d'informations qui lui sont propres.
Par exemple, dans un serveur FTP, pour stocker le répertoire racine du client pendant la connexion, on peut procéder ainsi :
- Au moment de la connexion, on crée un objet qu'on remplit avec les propriétés adéquates. On l'associe ensuite au thread client.
- Dans tout le reste du programme on pourra relire cette propriété dans les événements serveurs (puisque tous les événements ont accès au thread qui leur sont liés). On prendra garde à bien transtyper cette propriété dans le type choisi au départ pour l'objet.
- A la déconnexion, on libèrera l'objet.
On définit un objet à associer à chaque thread :
Code Delphi : | Sélectionner tout |
1 2 3 4 | TFtpClient = class(TObject) RootPath : String; // L'information à mémoriser Thread : Pointer; // Pointe vers le thread client end; |
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | procedure TMainForm.IdFTPServerAfterUserLogin(ASender: TIdFTPServerThread); var AClient : TFtpClient; begin AClient := TFtpClient.Create; // Création AClient.Thread := ASender; // Association Objet->Thread attaché AClient.RootPath := 'Donnée à mémoriser'; ASender.Data := AClient; // Associer l'objet au thread end; |
Code Delphi : | Sélectionner tout |
1 2 3 4 5 | procedure TMainForm.IdFTPServerMakeDirectory(ASender: TIdFTPServerThread; var APath : string; begin APath := StrGetRealPathName(TFTPClient(ASender.Data).RootPath,ASender.CurrentDir,VDirectory); ... |
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | procedure TMainForm.IdFTPServerDisconnect(AThread: TIdPeerThread); var AClient : TFtpClient; begin AClient := TFtpClient(AThread.Data); // On récupère l'objet if AClient <> nil then // Si le client a une structure associée... begin FreeAndNil(AClient); // on la détruit AThread.Data := nil; end; end; |
Il existe deux modes de communication : le mode connecté, le plus courant, correspondant au protocole TCP et le mode déconnecté correspondant au protocole UDP. Vous êtes invités à lire de la documentation appropriée pour plus de détails sur ces deux protocoles.
Pour utiliser le protocole UDP à partir de Delphi, nous pouvons utiliser les composants de la suite Indy, en particulier les composants IdUDPClient et IdUDPServer.
Le client envoie des données au serveur par l'intermédiaire de la méthode Send du composant IdUDPClient, comme nous sommes en mode déconnecté, nous devons préciser le destinataire du message ainsi que le port à chaque message :
Code Delphi : | Sélectionner tout |
IdUDPClt.Send('Host', 'Port', 'Le message à envoyer');
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | procedure TForm1.UDPSrvUDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle); var DataStringStream: TStringStream; begin DataStringStream := TStringStream.Create(''); try DataStringStream.CopyFrom(AData, AData.Size); ShowMessage('Reçu : "' + DataStringStream.DataString + '" De : ' + ABinding.PeerIP + ' sur le port : ' + IntToStr(ABinding.PeerPort)); finally DataStringStream.Free; 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.