TD 8 : Communication distribuée

Introduction      FAQ   TD 1   TD 2   TD 3   TD 4   TD 5   TD 6   TD 7   TD 8

0.  Mise à jour

N'oubliez pas de mettre à jour votre archive en téléchargeant l'archive forum.tar.gz et en y insérant vos fichiers client.c, server.c et common.c.

Quelques modifications ont été faite dans cette nouvelle version:

./client -v -s localhost

1.  Protocole client-serveur

Pour distribuer notre forum, nous utilisons un protocole rudimentaire: le client et le serveurs ne sont pas connectés en permanence. A la place, le client se connecte au serveur chaque fois que cela est nécessaire, envoie un premier message, et attend la réponse du serveur, avant de fermer la connexion.

1.1.  Message du client vers serveur

Le client envoie d'abord un entête composé de cinq caractères: le premier octect est soit nul, si aucun article n'est à envoyer au serveur, soit un, si un article est à envoyer. Les quatre caractères suivant représentent le numéro du dernier article reçu du serveur, en encodage LSB (Least Significant Byte first).

Si un message doit être envoyé au serveur, son contenu suit directement l'entête. Le client ferme la connexion en écriture une fois le message transmis.

1.2.  Réponse du serveur au client

Le serveur répond au serveur en envoyant d'abord un entête, puis le contenu d'un article si nécessaire (terminé par la fin de la connexion). L'entête est aussi composé de cinq octects: un premier octect est nul si aucun article n'est envoyé par le serveur au client, et vaut un si un article est transmis. Dans ce cas, les quatre octects suivant contiennent le numéro du message, suivis du contenu du message.

2.  Envoi d'un message au serveur (client_rpc)

Pour communiquer avec le serveur, le client utilise une fonction client_rpc:

void client_rpc(char *directory, char *msg_filename);

Cette fonction est appelé avec le répertoire directory contenant les messages du client. Le second argument msg_filename est soit NULL (si le client veut juste tester l'existence de nouveaux messages sur le serveur), soit contient le nom du fichier temporaire contenant le message à envoyer au serveur.

La fonction client_rpc effectue les opérations suivantes:

  1. Transformer le nom du serveur (variable globale server_hostname) en une adresse IP.
  2. Création d'une prise (socket) et connextion au serveur sur le port SERVER_PORT défini dans common.h .
  3. Calcul du numéro du dernier message reçu, en utilisant la fonction last_num définie dans un TP précédent.
  4. Envoi de l'entête au serveur, puis si nécessaire, du contenu de l'article à transmettre. Fermeture de la connexion en écriture seulement.
  5. Lecture de l'entête retourné par le serveur, et si nécessaire, écriture de l'article transmis par le serveur dans le répertoire du client.
  6. Répéter les étapes 2, 3, 4 et 5 tant que le serveur transmet des articles. On ne transmettra par contre qu'une seule fois l'article, si un article doit être envoyé au serveur.

Définissez votre fonction client_rpc pour votre client. Nouveaux appels système à utiliser: gethostbyname, socket, connect, shutdown.

On pourra avantageusement utiliser l'option -v du serveur pour afficher des informations sur ce qui est envoyé par le client.

3.  Envoi d'un message au client (server_rpc)

Pour communiquer avec le client, le serveur utilise une fonction server_rpc:

void server_rpc();

Cette fonction est appelée une fois au demarrage du serveur. Elle crée une prise qui attend sur le port SERVER_PORT des connexions du client. L'attente est effectuée par un thread, qui traite séquentiellement les connexions des clients.

Définissez votre fonction server_rpc. Nouveaux appels système à utiliser: bind, listen.

On pourra ensuite améliorer cette fonction en lançant un thread à chaque connexion d'un client. On pourra aussi utiliser les sémaphores pour éviter des accès concurrents à la variable globale current_num.