Sujet de TD/TP LWT

Transcription

Sujet de TD/TP LWT
Exercices sur les threads de la bibliothèque Ocaml
LWT
Antoine Kaszczyc
May 4, 2016
Contents
1 Je lance des alarmes
1
2 L’Utilisateur lance des alarmes
2
3 Question pour un netcat
3.1 Essai de connexion avec les joueurs . . . . . . . . .
3.2 Envoi d’une première question aux joueurs . . . . .
3.3 Réponse des joueurs . . . . . . . . . . . . . . . . .
3.4 Filtre des gagnants et perdants . . . . . . . . . . .
3.5 La question finale pour le plus rapide des netcat . .
3.5.1 Etape 1 : le plus rapide . . . . . . . . . . .
3.5.2 Etape 2 : le plus rapide mais qui a la bonne
1
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
réponse
.
.
.
.
.
.
.
2
4
4
5
5
5
6
6
Je lance des alarmes
• Ecrire une fonction qui prend un flottant f et renvoie un thread qui
attend n secondes avant d’afficher "Debout f !". Fonctions à utiliser :
– Lwt_unix.sleep :
– Lwt_io.write :
– Lwt_io.stdout :
float -> unit Lwt.t
Lwt_io.output_channel -> bytes -> unit Lwt.t
Lwt_io.output_channel
– Float.to_string
• Ecrire un code qui lance en "parallèle" trois alarmes avec des temps
différents. Fonctions à utiliser :
1
– Lwt.bind :
’a Lwt.t -> (’a -> ’b Lwt.t) -> ’b Lwt.t
– Lwt.join :
unit Lwt.t list -> unit Lwt.t
– Lwt_main.run :
’a Lwt.t -> ’a
• Ecrire une fonction récursive qui créé un join de f alarmes (de temps
1 à f), et la tester.
2
L’Utilisateur lance des alarmes
• Ecrire une fonction qui créé un thread qui récupère un flottant donné
par l’utilisateur et la tester. Fonctions à utiliser :
– Lwt_io.read_line :
– Lwt_io.stdin :
– Lwt.return :
Lwt_io.input_channel -> bytes Lwt.t
Lwt_io.input_channel
’a -> ’a Lwt.t
– Float.of_string
• Ecrire un code qui demande à l’utilisateur un flottant f et lance f
alarmes avec ce flottant (utiliser les fonctions écrites précédemment)
• Ecrire une fonction récursive recAlarm qui demande un temps à
l’utilisateur et lance une alarme, et recommence quand l’alarme est
finie.
• Ce qui est dommage, c’est que lorsque l’utilisateur a demandé une
alarme, il doit attendre qu’elle termine avant de pouvoir en lancer une
nouvelle. Ecrire une fonction qui prends en paramètre un nombre de
recAlarm à lancer en "parallèle".
• Ce qui est dommage, c’est qu’on est limités pour le nombre de recAlarm
en parallèle. Ecrire une fonction recAlarm2 qui attends un flottant,
et lorsqu’elle le reçoit, lance une alarme et sans attendre demande un
nouveau flottant.
3
Question pour un netcat
Cet exercice est prévu pour un TP.
L’objectif est d’approfondir avec les méthodes de Lwt_list et la fonction
Lwt.choose.
2
Le sujet est la réalisation d’un jeu de questions/réponses entre un serveur
et des participants (deux pour faire simple). La communication utilise des
sockets, la partie serveur est faite en ocaml grâce à la libraire Lwt_unix ; la
partie client est réalisée avec l’utilitaire netcat.
Tout d’abord définissons un type qui représentera les joueurs :
type player = Lwt_io.input_channel * Lwt_io.output_channel
Un joueur est donc représenté par un canal de communication d’entrée, et
d’un canal de sortie (entrée et sortie du point de vue du serveur, attention).
Voici la fonction qui créé un thread qui renvoie une liste de player :
let getPlayers n : player list Lwt.t =
let sockaddr = Lwt_unix.ADDR_INET (UnixLabels.inet_addr_loopback, 3003) in
let sock = Lwt_unix.socket Lwt_unix.PF_INET Lwt_unix.SOCK_STREAM 0 in
Lwt_unix.set_close_on_exec sock ;
Lwt_unix.setsockopt sock Lwt_unix.SO_REUSEADDR true ;
Lwt_unix.bind sock sockaddr ;
Lwt_unix.listen sock 3003 ;
let rec acceptPlayer n acc : player list Lwt.t =
if n > 0
then
let pt =
Lwt_unix.accept sock >>= fun (cliFD, _sock) ->
let inputChan = Lwt_io.of_fd ~mode:Lwt_io.input cliFD in
let outputChan = Lwt_io.of_fd ~mode:Lwt_io.output cliFD in
Lwt.return (inputChan, outputChan)
in
pt >>= fun p ->
acceptPlayer (n - 1) (p :: acc)
else
Lwt.return acc
in
acceptPlayer n []
Lisez-la un peu, et remarquez qu’elle créé les deux canaux, et qu’elle écoute
sur le port 3003 (à changer si besoin).
Et voici la fonction qui ferme la communication avec chaque joueur :
let closePlayers listPlayers =
Lwt_list.map_p
(fun (inputChan, _outputChan) ->
3
Lwt_io.close inputChan)
listPlayers
Voici enfin le code qui joue le rôle du serveur, il faudra le compléter au
fur et à mesure des exercices.
let _ =
Lwt_main.run
(* création des player *)
(let threadListPlayers = getPlayers 2 in
(* actions *)
threadListPlayers >>= fun listPlayers -> (* fonction stupide à changer *)
Lwt.return listPlayers
(* fermeture des player *)
(* on reprend "threadListPlayers" pour être sur de tous les fermer *)
>>= fun _ -> threadListPlayers >>= closePlayers)
Remarquez qu’on demande deux joueurs.
3.1
Essai de connexion avec les joueurs
Téléchargez le fichier du TP sur http://lipn.univ-paris13.fr/~kaszczyc/
l3-pp/tplwt.tar.gz. Vous trouverez un dossier déjà préparé dans lequel on
compile avec make (comme pour le devoir maison). Il y a un fichier OCaml
dans lequel est écrit le code qu’on vient de voir. Lancez make tplwt. Si tout
se passe bien, vous pouvez lancer l’exécutable qui s’est créé dans le dossier.
Si tout se passe bien, vous voyez le message "Attente des joueurs..". C’est le
moment de lancer les joueurs. Ouvrez deux nouveaux terminaux, et lancez
dans chaque la commande netcat localhost 3003. Selon la configuration,
vous aurez peut-être besoin de changer l’adresse ou le port (du côté ocaml
aussi). Si tout s’est bien passé, les trois programmes se ferment proprement
et vous pouvez passer à l’étape suivante.
3.2
Envoi d’une première question aux joueurs
Ecrivez la fonction sendMsg : string -> player list -> player list
Lwt.t qui envoie un message à chaque joueur. Regardez bien le type de la
fonction, elle est destinée à être composée avec bind.
Pour réaliser cette fonction, utilisez les fonctions suivantes :
4
• Lwt_list.map_p : (’a -> ’b Lwt.t) -> ’a list -> ’b list Lwt.t
qui applique un map à une liste de façon parallèle.
• Lwt_io.write_line :
Lwt.t
Lwt_io.output_channel -> bytes -> unit
• Lwt_io.flush : Lwt_io.output_channel -> unit Lwt.t à lancer
après avoir utilisé Lwt_io.write_line
Ensuite, ajoutez cette fonction au bloc Lwt_main.run afin d’envoyer la
question suivante aux joueurs : "vrai ou faux : OCaml est assez cool.".
Testez.
3.3
Réponse des joueurs
Ecrivez la fonction getAnswer : player list -> (player * string) list
Lwt.t qui attend la réponse de chaque player et lui associe sa réponse
string dans un thread qui contient la liste.
Pour réaliser cette fonction, utilisez la fonction suivante :
• Lwt_io.read_line qu’on a déjà vu
Testez.
3.4
Filtre des gagnants et perdants
Ecrivez la fonction filterWinners : string -> (player * string) list
-> player list Lwt.t qui prend la bonne réponse, et filtre les joueurs qui
ont donné la bonne réponse.
Vous utiliserez des fonctions
• Lwt_list.filter_p
• Lwt_list.map_p
N’hésitez pas à aller consulter la documentation http://ocsigen.org/lwt/
2.5.1/manual/ si besoin. Vous pouvez utiliser la fonction filter_map_p
pour faire plus joli.
Testez (la bonne réponse pour la question est "vrai").
3.5
La question finale pour le plus rapide des netcat
On passe maintenant à la question finale, et seul le player le plus rapide à
répondre sera le champion des netcat.
Avant tout, envoyez aux joueurs (seuls les gagnants de la question précédente) la question : "vrai ou faux : Macdo c’est très très bon.".
5
3.5.1
Etape 1 : le plus rapide
La première étape est de récupérer uniquement le premier joueur qui a
répondu, peu importe sa réponse. Utilisez pour cela la fonction Lwt.choose
pour écrire la fonction filterFaster : player list -> player list
Lwt.t. Faites très attention aux types de ces fonctions !!
Note : vous ne pouvez pas réutiliser la fonction getAnswer. Pourquoi ?
3.5.2
Etape 2 : le plus rapide mais qui a la bonne réponse
L’étape précédente n’était qu’un échauffement ; la vraie solution est un peu
différente. Le problème de getFaster est qu’il ne vérifie pas la validité de la
réponse. Ecrivez la fonction filterFasterCorrect : string -> player
list -> player list Lwt.t qui renvoie la liste contenant le premier joueur
ayant donné la bonne réponse (la liste peut être vide).
Utilisez pour cela la fonction :
• Lwt_nchoose_split qui sépare les joueurs ayant répondu des joueurs
ne l’ayant pas encore fait.
Repartez un peu de l’étape précédente, il y a des éléments similaires.
Pour terminer affichez le message "vous avez gagné" au champion des
player !
6