Vendredi 7 mars 2003
Dans ce td nous allons nous intéresser aux sockets utilisant le
protocole UDP. Ce protocole n'est pas fiable, il travaille en mode non
connecté et respecte les limites des messages. En d'autres termes, les
datagrammes émis peuvent être perdus ou dupliqués ; il est possible
d'envoyer des données même si personne n'est susceptible de les
recevoir et quand les données sont reçues elles le sont dans leur
intégralité.
Pour écrire un serveur utilisant UDP, il suffit de créer une prise de
type SOCK_DGRAM
et l'attacher à une adresse et à un
port1 donné au moyen de bind
. Le serveur peut alors recevoir
des données avec l'appel à la fonction
recvfrom
2. S'il y a suffisamment de
place3 dans le tampon de réception pour stocker les données, on est
sûr que la totalité des données a été reçue. Pour envoyer des données
il faut utiliser la fonction sendto
.
Pour créer un client, il suffit de créer une prise de type
SOCK_DGRAM
puis lire et écrire avec sendto
et recvfrom
. Notez
bien que les messages transmis ne peuvent pas être de taille
arbitrairement grande. Dans toutes les questions veillez à ne pas
dépasser une taille de 1024 octets en émission comme en réception.
Écrire une fonction
establish_udp_server
semblable à la fonction
establish_concurrent_server: (file_descr -> sockaddr -> string -> unit) -> int -> unit
du td précédent, où le premier argument est le descripteur sur lequel il faut
écrire, le deuxième est la prise depuis laquelle la connexion a été
initiée et le troisième contient ce que le client a écrit.
Lancez un serveur udp qui met en majuscule comme dans le td précédent.
Vous testerez votre serveur avec le client écrit dans la question suivante.
Écrivez un client udp qui se connecte sur votre serveur de la première
question, lui envoie ce qui est entré au clavier et affiche ce qu'il reçoit.
Pour être sûr que la réponse vient bien du serveur il est possible
d'utiliser une pseudo-connexion en appelant la fonction connect
.
Contrairement à son utilisation avec TCP, aucune communication n'a
lieu, tout est local. La pseudo-connexion assure que seuls les paquets
en provenance du serveur peuvent être reçu pendant la pseudo-connexion.
Les autres datagrammes sont éliminés.
Une fois qu'une prise UDP est pseudo-connectée il est possible
d'utiliser les méthodes read
et write
comme dans le cas de TCP.
Afin de ne pas bloquer indéfiniment sur un appel à read
qui ne
recevrait pas de datagramme, il est possible de placer un délai
maximum d'attente en lecture au moyen de la fonction système
setsockopt_float
grâce à l'option SO_RCVTIMEO
. En cas de délai
expiré l'exception Unix_error(EAGAIN,_,_)
est levée.
Pour tester votre programme vous pouvez essayer de contacter un port
d'une machine qui n'existe pas.
On désire créer un serveur de fichiers utilisant le protocole TFTP
(Trivial File Transfer Protocol) qui est largement utilisé,
dans les réseaux locaux, pour transférer les fichiers de
configuration. Nous nous restreignons ici à la lecture de fichiers
binaire (mode octet
) stockés sur le serveur. Le protocole TFTP
utilise le protocole de transport UDP, il faut donc gérer les pertes
de datagrammes en renvoyant, un certain nombre de fois les
datagrammes non reçus par les clients avant d'échouer. Pour cela,
comme dans l'exercice précédent, nous utiliserons pour les prises,
l'option SO_RCVTIMEO
.
Le protocole TFTP et le format des datagrammes est décrit dans la
RFC 1350 (rfc1350.txt). Le scénario
standard pour le serveur consiste à attendre les requêtes. Quand une
requête de lecture arrive, le serveur crée un nouvelle socket puis
émet un premier (numéro 1) datagramme contenant les 512 premiers
octets du fichier (ou moins si le fichier est plus petit). Il attend
alors l'acquittement du datagramme numéro 1. Quand il l'a reçu, il
peut émettre le tronçon suivant de 512 octets avec le numéro 2 et
attendre son acquittement, et ainsi de suite. La fin de fichier est
détectée quand un datagramme contenant un tronçon de moins de 512
octets est émis. À tout moment un datagramme d'erreur peut arriver
ou être envoyer pour terminer le transfert.
Les formats des datagrammes que nous allons traiter sont les
suivants :
-
requête de lecture du fichier
foo
en mode binaire
2 octets |
string |
1 octet |
string |
1 octet |
0 1 |
foo |
0 |
octet |
0 |
- données de numéro
i
2 octets |
2 octets |
n octets |
0 3 |
i |
data |
- acquittement des données de numéro
i
- erreur
2 octets |
2 octets |
string |
1 octet |
0 5 |
code |
msg |
0 |
Reportez-vous à la RFC pour avoir plus de détails sur le protocole
et le format précis des datagrammes.
Pour tester votre serveur vous pouvez utiliser le client tftp
accessible sous ~roussel/bin/tftp
. L'exemple suivant demande le
transfert du fichier foo
en mode binaire depuis le serveur attaché
au port 2000
de la machine locale.
Pour vérifier que votre serveur gère bien les pertes vous pouvez,
par exemple, simuler dans votre code la perte de certains
datagrammes émis et/ou reçus.
shell> ~roussel/bin/tftp localhost 2000
tftp> binary
tftp> get foo
This document was translated from LATEX by
HEVEA and HACHA.