Communications entre processus distants

Mise en oeuvre des sockets
(Interfaces de programmation C et Java)

B. Dupouy

On enverra le(s) fichier(s) source(s) traitant l'exercice et le makefile permettant leur bonne utilisation à :

dupouy@enst ou à gadret@enst
Le texte du message d'envoi doit contenir la chaîne TPsock

SOMMAIRE

Pour accéder à la documentation  Java :

I.Sockets : rappels

Documentation sockets au format pdf : sockets(500 Koctets)

I.1 Fonctionnement

On remarquera sur le schéma suivant :

II. Communication client/serveur en C

Le modéle client/serveur est le canevas sur lequel sont construites de nombreuses applications distribuées.

Dans ce modéle, le serveur se contente d'attendre les demandes de connexion des clients distants sur un port qualifié de port d'écoute. La gestion de la communication avec chaque client est prise en charge par un nouveau processus fils.
Le serveur, libéré de toutes les tâches de communication, se remet en attente de demande de connexions émanant d'autres clients.

Schéma de principe.
Après fin de l'attente sur accept, le serveur créé un processus fils, ce dernier fermera le socket d'écoute, tandis que le serveur fermera celui de communications :

Le serveur se libère ainsi de la gestion des communications pour se consacrer à l'écoute des demandes de connexions. Le nombre de ces demandes en attente est borné par listen , le nombre de communications en parallèle étant, lui, limité par le nombre de processus que le serveur peut créer :

Remarque :

II.1 Le serveur TCP en C

Ce serveur affiche à l'écran les messages émis par les clients en indiquant avec lequel de ses fils ils communiquent.

A faire pour le serveur :

Si le serveur est arrêté et relancé peu de temps après, il ne peut pas reprendre le même port d'écoute. Pour pouvoir réutiliser immédiatement ce port il faut utiliser la fonction setsockopt (après socket et avant bind) :
setsockopt(..., SOL_SOCKET, SO_REUSEADDR, ... , ... )

ATTENTION :
Sur une machine "System V" (par exemple, sur Solaris), ajouter les options suivantes lors de la compilation :
-lsocket (pour charger la bibliothèque des sockets)
-lnsl (pour charger les utilitaires htons...)

II.2 Le client TCP en C


A faire pour le client :


A faire :

  1. Faire interroger simultanément le serveur par plusieurs clients situés sur des machines différentes, ceci pour en vérifier le fonctionnement parallèle
  2. Dans l'exemple proposé pour le serveur, le signal SIGCHLD n'est pas géré. La commande ps permettra de voir les zombies résultant de la terminaison des processus fils du serveur.
  3. Utiliser la commande netstat : pour tracer les communications entre clients et serveur. Observer les autres communications entrantes et sortantes.

III. Utilisation de select.

La fonction select permet à une application de se mettre à l'écoute sur plusieurs sources simultanément, c'est à dire de multiplexer les entrées-sorties.
Nous allons étudier le fonctionnement d'un serveur utilisant select. Il attendra des entrées depuis le clavier et depuis quelques ports UDP.
Pour avoir les détails du mécanisme de select, utiliser la commande :
man -s 3c select

Pour comprendre le mécanisme de cet appel système, on va compléter le programme du client et du serveur dont on donne les squelettes ci-dessous.

III.1 Le programme à compléter pour le serveur UDP.

Comme le serveur TCP pécédent, ce serveur UDP se contente d'afficher à l'écran les messages émis par des clients UDP.

Cliquez ici pour consulter le serveur

III.2 Le programme à compléter pour les clients UDP.

Ce client envoie périodiquement des datagrammes vers un destinataire dont on lui a passé l'adresse IP et le numéro de port sur la ligne de commande.
Cliquez ici pour consulter le client

Travail à faire :

  1. Compléter les programmes serveur et client : voir les lignes où se trouvent des .... .
  2. Lancer le serveur, vérifier qu'il fait bien l'écho de ce qui est frappé au clavier
  3. Lancer un, puis plusieurs clients et vérifier que le serveur a bien reçu les datagrammes émis par les clients,

IV. Un serveur TCP Java multiplexé

Le serveur que nous allons construire attend des entrées depuis  : Voici son fonctionnement : L'utilisation des threads s'impose pour gérer les attentes simultanées sur ces différentes sources d'information.

IV.1 Exercice

Récupérer les trois fichiers : Serv_Mux.java, Serv_Clav.java, Serv_TCP.java.

Serv_Mux.java permet de lancer plusieurs threads :

Travail à faire :
  1. Lancer Serv_Mux, constater qu'il fait bien l'écho de ce qu'on lui entre au clavier.
  2. Voir les threads de type Thread_Standard se terminer les uns apres les autres.
  3. Interroger maintenant le serveur avec un client telnet (telnet nom-de-machine port) lancé depuis une autre fenêtre et ...
    ... vérifier que le serveur ne répond pas !!!
  4. Compléter le code de Serv_TCP pour qu'il réponde à un client telnet.
    Les lignes à modifier sont repérées par : // MODIF A FAIRE
  5. Interroger ce serveur modifié avec un client telnet  : il doit répondre par un écho du texte donné à telnet.

IV.2 Ajout d'un accès UDP

On va maintenant ajouter une écoute sur un port UDP.
Pour ce faire, on utilisera ce serveur UDP qui attend des messages sur le port 6666 et renvoie la date à l'émetteur.

Travail à faire :

  1. Modifier Serv_Mux.java pour mettre ce serveur UDP dans le tableau des threads créés.
  2. Relancer Serv_Mux modifié. Il écoute ainsi sur le clavier, des ports TCP et un port UDP.
  3. Pour tester le serveur UDP, utiliser le client UDP écrit en C de l'exercice du paragraphe III.2 .
    Lancé depuis une autre machine, on lui fait envoyer des datagrammes sur le port 6666.
  4. Arrêter Serv_Mux.
  5. Modifier ainsi le serveur UDP :
    Il renvoie toujours le datagramme reçu sur la machine émettrice, mais sur le port 12000.
  6. Vérifier le bon fonctionnement de l'application :
    1. Relancer Serv_Mux ainsi modifié
    2. Lancer alors le serveur C utilisant select, sur la même machine que le client UDP, en lui demandant d'attendre sur le port 12000.
    3. On doit constater que le message envoyé par le client UDP-C réveille le serveur UDP-Java qui envoie alors la date vers le serveur UDP-C sur le port 12000.

    Exemple de traces.

    Trace produite par le client UDP-C :

    Envoi de datagramme (Message emis par pid 7592 sur bajazet.enst.fr) vers ribouldingue
    
    Trace produite par le serveur UDP-Java :
    Serv_UDP-6666 va emettre : Tue Oct 02 16:45:29 GMT+0:00 2001 (taille : 33)
    
    Trace produite par le serveur UDP-C :
    (33) Tue Oct 02 16:45:39 GMT+0:00 2001
    

    Remarque :
    Ce programme Serv_Mux.java réalise l'équivalent d e la commande select.

    V. Synchronisation

    Dans le serveur TCP écrit en Java, on souhaite être averti de la terminaison d'un thread standard dès qu'elle se produit. Or, ce n'est pas le cas puisque join impose un ordre sur les réeceptions d'événement de fin des threads : vous avez sans doute constaté que main n'imprime pas de message lors de la fin d'un thread standard.

    V.1 Exercice facultatif

    Pour résoudre le problème, on va créer un outil de synchronisation qui propose les méthodes Attendre et Debloquer (qui utilisent respectivement wait et notify :

    Informations :

    A faire :

    ©(Copyright) dupouy@inf.enst.fr