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