Précédent Index Suivant

Prises de communication

Nous avons vus aux chapitre 3 et 4 deux moyens de communication entre processus, respectivement : les tubes et les canaux. Ces deux premières méthodes utilisent un modèle logique de concurrence. Ils n'induisent, en général pas d'améliorations de performances dans la mesure où les processus communicants partagent les mêmes ressource ; en particulier, le même processeur. La troisième possibilité, que nous présentons dans ce paragraphe, utilise les prises de communication ou sockets1. Cette possibilité vient du monde Unix. Elle permet la communication entre processus tournant soit sur une même machine, soit sur différentes machines.

Description et création

Une prise de communication a pour rôle dans l'existence d'établir une communication avec une autre prise dans le but de transférer des informations. Nous listons les différentes situations que l'on peut rencontrer ainsi que les commandes et types qui seront utilisés par les sockets TCP/IP. La métaphore classique est de comparer les sockets aux postes téléphoniques.
Domaine
Suivant qu'une prise est destinée à la communication interne ou externe elles appartiennent à un domaine différent. La bibliothèque Unix définit deux domaines possibles correspondant aux constructeurs du type :

# type socket_domain = PF_UNIX | PF_INET;;
Le premier domaine correspond à la communication locale et le second, à la communication transitant sur le réseau Internet. Ce sont là les principaux domaines d'application des sockets. Nous n'utiliserons dans la suite que les sockets du domaine Internet.

Type et protocole
Quelque soit leur domaine, les sockets définissent certaines propriétés de communications (fiabilité, ordonnancement, etc.) représentées par les constructeurs du type :

# type socket_type = SOCK_STREAM | SOCK_DGRAM
| SOCK_SEQPACKET | SOCK_RAW ;;
Suivant le type de socket utilisé, le protocole sous-jacent à la communication obéira aux caractéristiques définies. À chaque type de communication est associé un protocole par défaut.

En fait, nous n'utiliserons ici que le premier type de communication : SOCK_STREAM avec le protocole par défaut TCP. Il garantit fiabilité, ordonnancement et non duplication des messages échangés et fonctionne en mode connecté.

Pour le reste, nous renvoyons le lecteur à la littérature sur Unix, par exemple [Rif90].

Création
La fonction de création d'une socket est :

# Unix.socket ;;
- : Unix.socket_domain -> Unix.socket_type -> int -> Unix.file_descr = <fun>
Le troisième argument (de type int) permet de préciser le protocole associé à la communication. La valeur 0 est interprétée comme << le protocole par défaut >> associé au couple (domaine,type), arguments de la création de la socket. La valeur de retour de cette fonction est un descripteur de fichier. Ainsi les échanges pourront se faire en utilisant les fonctions standards, du module Unix, d'entrées-sorties.

Créons une socket TCP/IP :

# let s_descr = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 ;;


Warning


Bien que la fonction socket retourne une valeur de type file_descr, le système fait la différence entre un descripteur de fichier classique et celui attribué à une socket. L'intérêt est de pouvoir utiliser les fonctions sur les fichiers du module Unix sur les sockets. Une exception est déclenchée dans le cas où un descripteur classique est passé à une fonction attendant une socket.



Fermeture
Comme tout descripteur de fichier, une socket se ferme par la fonction :

# Unix.close ;;
- : Unix.file_descr -> unit = <fun>
La sortie par exit d'un processus ferme les descripteurs de fichier encore ouverts.

Adresses et connexions

Une socket que l'on vient de créer ne possède pas d'adresse. Pour qu'une connexion puisse s'établir entre deux sockets, l'appelant doit connaître l'adresse du récepteur.

L'adresse d'une socket (TCP/IP) est constituée d'une adresse IP et d'un numéro de port. correspondant au type suivant :

# type sockaddr =
ADDR_UNIX of string | ADDR_INET of inet_addr * int ;;
L'entier qui fait partie de l'adresse des sockets du domaine Internet correspond au numéro de port.

Attribution d'une adresse
La première chose à faire, pour pouvoir recevoir des appels, après la création d'une socket est de lui attribuer, de la lier à, une adresse. C'est le rôle de la fonction :

# Unix.bind ;;
- : Unix.file_descr -> Unix.sockaddr -> unit = <fun>
En effet, nous avons déjà un descripteur de socket, mais l'adresse qui lui est attachée à la création ne peut guère nous être utile comme le montre l'exemple suivant :

# let (addr_in, p_num) =
match Unix.getsockname s_descr with
Unix.ADDR_INET (a,n) -> (a,n)
| _ -> failwith "pas INET";;
val addr_in : Unix.inet_addr = <abstr>
val p_num : int = 0
# Unix.string_of_inet_addr addr_in;;
- : string = "0.0.0.0"
Reste donc à fabriquer une adresse utile et l'associer à notre socket. Nous reprenons notre adresse locale my_addr obtenue page X et choisissons le port 12345 qui, en général, n'est pas attribué.

# Unix.bind s_descr (Unix.ADDR_INET(my_addr, 12345)) ;;
- : unit = ()


Capacité d'écoute et réception
Il faut encore procéder à deux opérations avant que notre socket soit complètement opérationnelle pour recevoir des appels : définir sa capacité d'écoute et se mettre effectivement à l'écoute. C'est le rôle respectif des deux fonctions :

# Unix.listen ;;
- : Unix.file_descr -> int -> unit = <fun>
# Unix.accept ;;
- : Unix.file_descr -> Unix.file_descr * Unix.sockaddr = <fun>
Le second argument (de type int) de la fonction listen indique le nombre maximal de connexions en attente toléré. L'appel de la fonction accept attend une demande de connexion. Quand celle-ci survient la fonction accept se termine et retourne l'adresse de la socket ayant appelée ainsi qu'un nouveau descripteur de socket, dite socket de service. Cette socket de service est automatiquement liée à une adresse. La fonction accept ne s'applique qu'aux sockets ayant exécuté un listen, c'est-à-dire aux sockets ayant paramétrées la file d'attente des demandes de connexion.

Demande de connexion
La fonction duale de accept est ;

# Unix.connect ;;
Un appel à Unix.connect s_descr s_addr établit un connexion entre la socket locale s_descr (qui est automatiquement liée) et la socket d'adresse s_addr.

Communication
À partir du moment où une connexion est établie entre deux sockets, les processus propriétaires de celles-ci peuvent communiquer dans les deux sens. Les fonctions d'entrées-sorties sont celles du module Unix décrites au chapitre 3.


Précédent Index Suivant