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.
- Qu'est-ce qu'un socket ?
- Qu'est-ce qu'un socket bloquant ?
- Qu'est-ce que Indy ?
- Comment utiliser les Command Handlers ?
- Pourquoi n'y a t-il pas d'évènements OnError dans les sockets Indy ?
- Comment différencier erreurs VCL et erreurs Indy ?
- À quoi sert la propriété Bindings ?
- Comment déboguer des applications Indy ?
- Comment soumettre un formulaire de type file avec Indy ?
- Comment utiliser le SSL avec Indy ?
Un socket est l'abstraction d'un objet via lequel un service peut envoyer ou recevoir des données. Il permet la connexion au réseau de l'application et sa communication avec les autres entités connectées.
Les sockets se composent de deux familles principales : les sockets de stream (stream sockets) et de datagrammes (datagrams sockets). Les sockets de datagrammes utilisent UDP pour envoyer rapidement des messages qui n'excèdent pas 65000 octets de longueur, alors que les sockets de streams permettent de recevoir de manière fiable des flux de données avec TCP.
Sous Windows, la gestion des sockets est effectuée par un ensemble de fonctions (API) nommées Winsock (WINdows SOCKets), actuellement en version 2 (ws2_32.dll). Delphi encapsule ces API avec les composants basiques TTCPServer et TTCPClient. Cependant, ces composants sont vraiment peu flexibles et peu évidents. De nombreuses librairies plus pratiques se sont développées pour Delphi. Les deux plus connues sont :
- ICS, de Francois Piette : http://www.overbyte.be/frame_index.html
- Indy, dont il est question ici : http://www.indyproject.org/index.en.aspx
Il y a deux modes d'exploitation des sockets : le mode bloquant et le mode non bloquant (Winsock supporte les 2 modes). Le principe du mode bloquant (Indy) est le suivant : une fonction lisant ou écrivant sur un socket ne rend pas la main tant qu'elle n'a pas terminé ses opérations. Si un fichier doit être envoyé via un canal TCP, la fonction l'envoyant ne rendra pas la main tant qu'elle n'aura pas terminé l'envoi, ou qu'une erreur survienne pendant cet envoi. C'est, ceci dit, le comportement normal de toute fonction Delphi : cela se passe de la même manière lorsque vous lisez ou vous écrivez dans un fichier, à part que les temps d'accès sur le réseau sont nettement plus longs que sur votre disque. On utilisera des techniques spéciales pour ne pas que toute l'application soit figée en phase bloquante.
Le mode non bloquant (implémenté dans la librairie internet ICS de Francois Piette) fonctionne de manière opposée. Par exemple, l'exécution n'est pas stoppée en l'attente de données si vous lisez le socket alors qu'il n'y a aucune donnée disponible. Des événements sont déclenchés quand il y a quelque chose à lire et quand ce qu'il y a à écrire a été écrit. Lorsque vous voulez envoyer quelque chose, la fonction rend immédiatement la main et Windows renvoie un message l'opération terminée, que vous pouvez éventuellement prendre en compte dans un événement du composant. Cette approche événementielle est plus difficile à utiliser au début (à mon avis ) car on doit toujours utiliser les événements du serveur pour connaître l'état des opérations précédentes alors qu'un simple gestionnaire d'erreurs suffit en mode bloquant.
Le débat entre sockets bloquants et sockets non bloquants a fait couler beaucoup d'encre. Le fait que le mode bloquant impose l'utilisation de threads (du moins, du côté serveur) peut faire dire à certains qu'au final, lors d'utilisations intensives, le système passera plus de temps à basculer entre les threads qu'à traiter concrètement les requêtes. A ce sujet, le concepteur d'Indy répond que les threads ne sont basculés que lorsqu'ils sont actifs alors que les sockets sont la plupart du temps en phase d'attente, c'est donc un faux problème.
Le côté bloquant a de nombreux avantages dont le premier est la simplicité de conception et de débogage : toutes les transactions sont écrites en une seule procédure, on n'a pas besoin de gérer des connexions par des séquences d'événements. Le mode bloquant est très adapté aux threads. De plus, les sockets bloquants sont les seuls supportés sous Linux. Indy ayant été porté pour Linux, si vous voulez construire des applications multi-plateformes, c'est sans doute un bon choix que de prendre Indy comme base sous Kylix.
Indy et ICS représentent chacun de leur côté une excellente implémentation des deux modes de fonctionnement des sockets. Essayez les deux, et faites votre choix.
Indy est une librairie de composants Open Source disponible pour Delphi et Kylix (Linux). C'est un ensemble d'environ 70 objets prenant en charge la majorité des protocoles client et serveur.
Indy fut d'abord développé sous Visual Basic en 1993 comme un jeu de fonctions par Chad Z. Hower. Porté sous Delphi, initialement sous le nom de Winshoes, il fut ouvert à l'open source en 1995, puis inclus dans Delphi 6 et 7. La version 8 de Winshoes, dont l'objectif principal était la compatibilité avec Kylix, a été renommée en Indy (Internet Direct) pour perdre le lien original du nom avec Windows (Winshoes était un jeu de mots avec Winsocks, l'implémentation Win32 des sockets). Indy est maintenant maintenue par une équipe de professionnels et désormais porté pour la plateforme .NET.
Deux versions sont à ce jour disponibles : la version 9 et la version 10. La version 9 est considérée comme stable, la version 10 est une refonte complète du noyau effectuée tout en tentant de garder une compatibilité minimum avec la version 9. De nombreux nouveaux protocoles sont prévus, les performances améliorées via l'utilisation de fibers (sorte de threads dont la gestion n'est pas assurée par l'OS), etc. Pour l'instant cette version est en béta-test, on ne l'utilisera donc pas, et on se concentrera sur la version 9.
Les CommandHandlers ont été introduits pour simplifier l'implémentation de serveurs qui doivent réagir à des commandes textuelles comportant des paramètres éventuels. Ils peuvent être utilisés pour générer des messages de réponse ou appeler des procédures pour réaliser ce que la commande est sensée faire.
Si, par exemple, vous voulez implémenter une commande permettant d'éteindre un ordinateur à distance, vous pouvez enregistrer un handler pour la commande SHUTDOWN sur le serveur, qui sera donc déclenché quand la commande sera reçue. Vous ne répondrez donc pas à la commande dans OnExecute, mais dans l'événement OnCommand du CommandHandler créé.
Pour utiliser les CommandHandlers, la propriété CommandHandlerEnabled des descendants du TIdTCPServer doit être mise à true. Ensuite, vous devez ajouter des items dans la collection "CommandHandlers". Changez la propriété "Command" de ces items avec le nom de la commande à laquelle votre serveur devra réagir. Tapez ensuite le code à exécuter dans l'événement OnCommand du CommandHandler créé.
Exemple de code :
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 | procedure TForm1.IdTCPServer1TIdCommandHandler0Command( ASender: TIdCommand); var Param1,Param2 : string; begin with ASender.Thread.Connection do try if ASender.Params.Count < 2 then // Accès aux paramètres // Envoi d'une réponse avec WriteLn WriteLn('Pas assez de paramètres !') else begin Param1 := ASender.Params[0]; Param2 := ASender.Params[1]; // Faire quelque chose end; finally // Au final, par exemple, déconnecter. Disconnect; end; end; |
Contrairement à certains sockets, les composants Indy n'ont pas d'événement OnError. En fait, les concepteurs ont privilégié la voie « naturelle » de gestion des exceptions : lorsqu'une erreur doit être notifiée, une exception est levée, comme tout autre composant de la VCL. A vous, par conséquent, d'encadrer les blocs de code susceptibles de produire des erreurs par les structures de contrôle try except end et try finally end.
Les exceptions Indy descendent toutes de EIdException. On peut donc différentier les erreurs liées à la connexion et celles de la VCL en construisant un gestionnaire d'erreurs de ce style :
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 | Try //Code ici Except On E : EIdException do MessageDlg(Format('Erreur Indy : %s',[E.message]), mtError, [mbOK], 0) On E : Exception do MessageDlg(Format('Autre type d''erreur : %s',[E.message]), mtError, [mbOK], 0); End; |
Par défaut, votre serveur est attaché au port choisi avec toutes les adresses IP disponibles sur le poste (Indy utilise l'IP générique 0.0.0.0). Modifiez cette propriété pour associer votre service à une seule ou plusieurs IP déterminées. Par exemple, si vous souhaitez que votre serveur web ne puisse être accessible que par votre machine (ceci par exemple pendant les phases de test), vous pouvez associer uniquement votre IP locale à la propriété Bindings. Par code, cela donnerait :
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 | IdHTTPServer.Bindings.Clear; with IdHTTPServer.Bindings.Add do begin IP := '127.0.0.1'; Port := 80; end; |
Lorsque vous voulez tester des protocoles orientés "commande" qui ne nécessitent pas de communications binaires, pensez à tester votre application avec Telnet ! Telnet est un socket client qui ne fait que transmettre vos requêtes et afficher les réponses du serveur, c'est le moyen idéal de voir ce qu'il vous répond. Le client telnet se démarre sous Windows par la ligne de commande suivante :
Code Delphi : | Sélectionner tout |
telnet <Hôte> <IP>
Code Delphi : | Sélectionner tout |
telnet localhost 80
Vous pouvez parfois avoir à envoyer des flux binaires à un serveur via un formulaire : la majorité du temps, il s'agit de formulaires d'upload de fichiers qui copient un fichier de votre PC vers le serveur. Nous allons voir un cas pratique : la réalisation d'un tel système en HTML/PHP puis la simulation du formulaire HTML avec Indy.
Le code HTML du formulaire (côté client)
C'est un formulaire classique, contenant deux champs : un pour contenir le chemin du fichier à uploader (userfile), l'autre pour contenir le dossier de destination sur le serveur (dossier). Les données sont envoyées par POST, directement dans la requête HTTP.
Code HTML : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | <HTML> <BODY> <FORM method="post" enctype="multipart/form-data" action="http://127.0.0.1/upload/uploadfile.php"> <INPUT type="hidden" name="MAX_FILE_SIZE" value="2000000" /> Dossier : <INPUT type="text" id="dossier" name="dossier" size="20" /> <BR /> Fichier : <INPUT type="file" id="userfile" name="userfile" size="20" /><BR /><BR /> <INPUT type="submit" value="Envoyer..." /> </FORM> </BODY> </HTML> |
Placé dans le dossier /upload, le script effectue les actions suivantes :
- Récupérer et traiter l'adresse de destination,
- Créer le dossier où sera stocké le fichier (si nécessaire),
- Vérifier l'intégrité du fichier (en comparant sa taille théorique et la taille du fichier reçu),
- Renommer les fichiers PHP en TXT pour éviter des attaques suivant le mode du "cheval de troie",
- Déplacer le fichier reçu dans le bon répertoire (/upload).
Code PHP : | 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 | <?php $dossier=$_POST['dossier']; if($dossier<>'') { if(substr($dossier,strlen($dossier)-1,1)=='/')$dossier.='/'; $dir=@explode('/',$dossier); $dossier=''; for($x=0;$x<count($dir);$x++) { $dossier.=$dir[$x].'/'; if(! @is_dir($dossier))@mkdir($dossier,0777); } if(! @is_dir($dossier))die("Le dossier est invalide! (".$dossier.")"); } $filenameHTTP=$HTTP_POST_FILES['userfile']['name']; $typeHTTP=$HTTP_POST_FILES['userfile']['type']; $sizeHTTP=$HTTP_POST_FILES['userfile']['size']; $tmpfileHTTP=$HTTP_POST_FILES['userfile']['tmp_name']; if((empty($filenameHTTP))or($sizeHTTP<=0)) die("Le fichier spécifié est introuvable ou vide!"); if(@is_uploaded_file($tmpfileHTTP)) { if(@eregi('.php',$filenameHTTP))$filenameHTTP.='.txt'; if(filesize($tmpfileHTTP)<>$sizeHTTP) die("Erreur de téléchargement du fichier!"); if(@move_uploaded_file($tmpfileHTTP,$dossier.$filenameHTTP)) { @chmod($filenameHTTP,0777); echo "Fichier correctement uploadé!<br>". $dossier.$filenameHTTP." (".round($sizeHTTP/1024)." ko)<br>"; } else die("Une erreur est survenue lors du téléchargement!"); } else die("Erreur de téléchargement du fichier!"); ?> |
Basiquement, il s'agit de "remplir" les champs du formulaire comme vous le faites depuis un navigateur Web. On doit donc renseigner le champ "dossier" et "userfile". Pour cela, on utilise un objet TIdMultiPartFormDataStream (ajoutez IdMultipartFormData dans votre clause uses). Les champs "normaux" sont remplis avec la méthode AddFormField qui attend un nom de champ et une valeur. Le champ contenant le flux binaire à envoyer au serveur est quand à lui rempli avec la méthode AddFile qui attend elle, un nom de champ, l'adresse du fichier à joindre et le type MIME des données à joindre.
Enfin, on poste la requête en appellant Post() paramétré avec l'adresse du script PHP ainsi que le IdMultipartFormData crée :
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 | uses IdMultipartFormData, ... ... procedure TForm1.Button1Click(Sender: TObject); var MultiPartFormDataStream: TIdMultiPartFormDataStream; begin if OpenDialog1.Execute then begin MultiPartFormDataStream := TIdMultiPartFormDataStream.Create; try //"Remplis" la variable "dossier" pour spécifier l'upload dans /test MultiPartFormDataStream.AddFormField('dossier','/test'); //"Joint" le fichier MultiPartFormDataStream.AddFile('userfile', OpenDialog1.FileName, 'multipart/form-data'); MultiPartFormDataStream.Position := 0; //Poste ! IdHTTP1.Post('http://127.0.0.1/upload/uploadfile.php', MultiPartFormDataStream); finally MultiPartFormDataStream.Free; end; end; end; |
Il faut récupérer les deux DLL OpenSSL selon la version de indy que l'on utilise :
- Indy 9 : http://indy.fulgan.com/SSL/
- Indy 10.1.5 : http://www.apachefrance.com
Ensuite, il suffit de poser un composant IdSSLIoHandlerOpenSll et le lier au composant Indy par le IOHandler (IdSmtp , IdHttp, etc.).
Les DLLs doivent être dans le répertoire de l'application ou dans le répertoire System32 de Windows.
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.