Précédent Index Suivant

Protocoles de communication

Les différentes communications des clients-serveurs décrits à la section précédente consistent à envoyer une chaîne de caractères terminée par un retour-chariot et à en recevoir une autre. Bien que simples ce sont déjà des protocoles. En effet un récepteur s'attend à recevoir une chaîne terminée par un caractère spécial. Dès que l'on veut communiquer des valeurs plus complexes : un flottant une matrice de flottants, un arbre d'expressions arithmétiques, une fermeture ou un objet, se pose le problème du codage de ces valeurs. Plusieurs solutions existent selon la nature des programmes communicants que l'on caractérise par : le langage d'implantation, l'architecture machine et dans certains cas le système d'exploitation. En effet selon l'architecture machine les entiers seront représentés de différentes manières (bits de poids fort à gauche, à droite, bits de tag, taille du mot machine). Comment alors communiquer une valeur entière cohérente pour d'autres programmes sur d'autres architectures. Il devient alors nécessaire d'avoir une représentation ``externe'' des valeurs2. De plus pour les valeurs plus structurées comme un enregistrement, là aussi la représentation interne doit pouvoir <<s'externaliser>> mais de plus certains langages autorisent des constructions, comme les champs de bits, qui n'existent pas dans les autres langages. Le passage de valeurs fonctionnelles ou d'objets, qui contiennent une partie code, pose une nouvelle difficulté sur le chargement de ce code. Celui-ci est-il compatible (code-octet) entre l'envoyeur et le récepteur, et existe-t-il un mécanisme de chargement dynamique de code? En règle générale on simplifiera ce problème en supposant que le code existe des deux cotés et celui-ci ne sera pas transmis. Ce qui signifie que pour les objets la même classe existe chez le receveur. Pour les fermetures, que le code correspondant au function existe et est à la même place, ce qui implique que ce sont deux mêmes programmes qui communiquent.

Les protocoles texte, c'est à dire la communication en format ASCII, sont les plus courants car les plus simples à mettre en oeuvre et les plus portables. Par contre quand le protocole se complexifie ils peuvent être assez lourds à implanter. Dans ce cadre là on définit une grammaire pour décrire le format de communication. Celui peut être riche, mais ce sera aux programmes communicants de faire le travail de codage et d'interprétation des chaînes reçues.

En règle générale, l'utilisation d'une application réseau ne permet pas voir les différentes couches de protocoles utilisées. C'est typiquement le cas du protocole HTTP permettant à un navigateur de communiquer avec un site Web.

Protocole HTTP

On rencontre fréquemment le terme <<HTTP>> dans les publicités. Il correspond au protocole de communication des applications du Web. Celui-ci est complètement décrit sur la page du consortium W3 :

Lien


http://www.w3.org
Il est utilisé pour transporter les requêtes émises par les navigateurs (Explorer, Communicator, Opera, etc) et pour retourner le contenu de la page demandée. Une requête effectuée par un navigateur contient le nom du protocole (HTTP), le nom de la machine (www.ufr-info-p6.jussieu.fr), et l'arboressence de la page demandée (/Public/Localisation/index.html). Cet ensemble de trois composants permet de définir une URL (Uniform Ressource Locators) :
http://www.ufr-info-p6.jussieu.fr/Public/Localisation/index.html
permettant d'avoir accès aux ressources du Web. Quand une telle URL est demandée à partir d'un navigateur, une connexion via une socket est établie entre le navigateur et le serveur s''exécutant sur la machine indiquée par défaut sur le port 80. Puis le navigateur envoie une requête au format HTTP, proche de la suivante :
GET /index.html HTTP/1.0
Alors le serveur lui répond dans le protocole HTTP, l'entête :
HTTP/1.1 200 OK
Date: Wed, 14 Jul 1999 22:07:48 GMT
Server: Apache/1.3.4 (Unix) PHP/3.0.6 AuthMySQL/2.20
Last-Modified: Thu, 10 Jun 1999 12:53:46 GMT
ETag: "4c801-e4f-375fb55a"
Accept-Ranges: bytes
Content-Length: 3663
Connection: close
Content-Type: text/html
Cet entête indique que la requête est acceptée (code 200 OK), la nature du serveur, la date de modification de la page, la longueur de la page envoyée et son type du contenu qui va suivre. Sans la précision de la version de protocole (HTTP/1.0) de la commande GET, seule la page HTML est transférée. La connexion suivante avec telnet permet de voir ce qui est effectivement transmis :
$ telnet www.ufr-info-p6.jussieu.fr 80
Trying 132.227.68.44...
Connected to triton.ufr-info-p6.jussieu.fr.
Escape character is '^]'.
GET


<!-- index.html -->
<HTML>
<HEAD>
<TITLE>Serveur de l'UFR d'Informatique de Pierre et Marie Curie</TITLE>
</HEAD>
<BODY>

<IMG SRC="/Icons/upmc.gif" ALT="logo-P6" ALIGN=LEFT HSPACE=30>
Unit&eacute; de Formation et de Recherche 922 - Informatique<BR>
Universit&eacute; Pierre et Marie Curie<BR>
4, place Jussieu<BR>
75252 PARIS Cedex 05, France<BR><P> 
....
</BODY>
</HTML>
<!-- index.html -->

Connection closed by foreign host.
La connexion se referme dès que la page est copiée. On s'aperçoit qu'un navigateur reçoit une telle information et arrive à l'afficher de manière formatée. Le protocole de base est en mode texte, et le langage à interpréter aussi. On note que les images ne sont pas transmises avec la page. C'est au navigateur, lors de l'analyse syntaxique de la page HTML, de remarquer les ancres et les images (voir la balise IMG de la page transmise). À ce moment là, le navigateur envoie une nouvelle requête par image rencontrée dans le source HTML. Quand ces images sont transmises, elles sont affichées. Il y a donc une nouvelle connexion par image. C'est pour cela que souvent les images s'affichent en parallèle.

Le protocole HTTP est ``assez'' simple, mais il véhicule une information dans le langage HTML qui est plus complexe et peut être source de nombreuses nouvelles connexions. La plupart des navigateurs lancent d'ailleurs les différentes connexions de manière concurrente.

Protocoles avec acquittement et limite de temps

Quand le protocole est complexe, il est utile que le récepteur d'un message indique à l'envoyeur qu'il a bien reçu le message et que celui-ci est grammaticalement correct. En effet le client peut être en attente bloquante de réponse avant de poursuivre ses taches. Si la partie du serveur traitant cette requête a une difficulté d'interprétation du message, le serveur doit l'indiquer au client plutôt que d'oublier cette requête. L'exemple du protocole HTTP a une gestion de code d'erreurs. Une requête correcte renvoie le code 200. Une requête mal formée ou la demande d'accès à une page non autorisée renvoie un code d'erreur 4xx ou 5xx selon la nature de l'erreur. Ces codes d'erreur permettent au client de savoir ce qu'il a à faire et au serveur de tenir des fichiers de compte-rendu précis sur la nature des requêtes.

Lorsqu'un serveur est dans un état incohérent, il peut toujours accepter une connexion d'un client, mais risque de ne jamais lui envoyer de réponse sur la socket. Pour éviter ces attentes bloquantes, il est utile de fixer une limite de temps pour la transmission de la réponse. Passé ce délai, le client suppose que le serveur ne répondra plus. Alors le client peut fermer cette connexion pour passer à la suite de son travail. C'est ainsi que les navigateurs WWW font. Quand une requête n'a pas de réponse au bout d'un certain temps, le navigateur décide de l'indiquer à l'utilisateur. Objective CAML possède des entrées-sorties avec limite de temps. Dans le module Thread, les fonctions wait_time_read et wait_time_write qui suspendent l'exécution de la tache jusqu'au moment où un caractère peut être lu ou écrit, cela dans un certain délai. Elles prennent en entrée un descripteur de fichier et un délai exprimé en secondes : Unix.file_descr -> float -> bool. Quand le retour vaut true alors l'entrée-sortie peut s'effectuer, sinon le délai a été dépassé.

Transmission de valeurs en représentation interne

Les difficultés inhérentes à l'envoi-réception de valeurs en représentation interne sont les mêmes que celles rencontrées pour les valeurs persistantes (voir le module Marshal page ). En effet stocker une valeur dans un fichier puis la relire plus tard du même ou d'un autre programme revient au même que d'envoyer une valeur sur une socket et de la lire à l'autre bout de la prise. Il faut donc alors que les deux programmes connaissent le nom du la valeur, pour que le receveur puisse forcer la valeur transmise, et que ce nom corresponde bien à la même définition. L'intérêt est de simplifier le protocole. Il n'y a plus besoin de coder des données dans un format texte. Par contre le typage est moins sûr car rien n'empêche d'écrire un client se connectant à un tel serveur et lui envoyant une donnée d'un autre type. De plus cela limite les communications à des programmes écrits en Objective CAML. On montre sur deux exemples comment effectuer cette communication.

Exemple : service calcul matriciel

On définit un petit service de calcul matriciel. Le client envoie au serveur le nom de l'opération désirée (par exemple ``PLUS'' ou ``MULT'') ainsi que les matrices en représentation interne. Le serveur renvoie le résultat de ce calcul. On reprend le squelette du serveur ``majuscules'' en changeant la fonction de traitement.

Valeurs fonctionnelles

Dans le cas de transmission d'une fermeture entre deux programmes Objective CAML, le code de la fermeture n'est pas envoyé, seul son environnement et son pointeur de code (voir ) le sont. Pour que cela fonctionne il est nécessaire que le serveur possède le même code au même emplacement. Ce qui implique que le même programme tourne en tant que serveur et en tant que client. Rien n'empêche cependant que ces deux programmes soient en train d'exécuter des parties de code différentes. On adapte alors le service ``calcul matriciel'' en envoyant la fermeture contenant dans son environnement les données à calculer. À la réception le serveur applique cette fermeture à () et le calcul se déclenche.

Pour améliorer ce service, on peut définir un type pour la gestion des exceptions du coté du serveur :

# type 'a valeur = V of 'a | E of exn;;
type 'a valeur = | V of 'a | E of exn
Le serveur transmet alors ue valeur de ce type. Le client la filtre et selon le cas utilise la valeur retournée ou déclenche l'exception transmise.

Inter-opérer avec d'autres langages

L'intérêt des protocoles texte est qu'ils sont indépendant des langages d'implantation des clients et des serveurs. En effet le code ASCII est toujours connu des langages de programmation. Ensuite c'est au client et au serveur de savoir analyser syntaxiquement les chaînes de caractères transmises. On discute sur l'exemple d'un tel protocole ouvert est la simulation de joueurs de football, appelée RoboCup.

Robots footballeurs

Une équipe de foot joue contre une autre équipe. Chaque membre d'une équipe est un client du serveur ``arbitre''. Les joueurs d'une même équipe ne peuvent pas communiquer directement entre eux. Ils doivent passer par le serveur, qui retransmet le dialogue. Le serveur indique selon la position du joueur une partie du terrain. Toutes ces communications s'effectuent selon un protocole texte. La page de présentation du protocole, du serveur et de certains clients :

Lien


http://www.robocup.org/
Le serveur est écrit en C. Les clients sont écrits dans différents langages : C, C++, Smalltalk, Objective CAML, ...Rien n'empêche d'ailleurs à une équipe de posséder des joueurs issus de différents langages.

Ce protocole répond bien aux besoins d'inter-opérabilité entre programmes de différents langages d'implantation. Il est relativement simple, mais il nécessite un analyseur syntaxique particulier pour chaque famille de langages.






Précédent Index Suivant