PROFDINFO.COM

Votre enseignant d'informatique en ligne

Utilisation de sockets asynchrones

Pour ce laboratoire, vous allez devoir développer un client et un serveur de chat. Le serveur n'est simplement là que pour relayer les messages d'un client aux autres.

Inspirez-vous du code fourni pour la démonstration des sockets asynchrones comme point de départ.

Quelques points à considérer

  • Le serveur et le client doivent tous les deux pouvoir envoyer et recevoir des messages de façon asynchrone.
  • C'est beaucoup plus simple si vous utilisez des buffers différents pour envoyer et recevoir des messages. Utilisez le buffer du SocketPacket pour recevoir et un buffer différent pour envoyer.
  • N'oubliez pas de réinitialiser un buffer après usage pour éviter de rester pris avec des caractères dedans ou avec sa taille précédente!
  • Un client ne peut se connecter qu'à un seul serveur mais un serveur peut accepter un nombre infini de clients.
  • Faites attention aux firewalls lorsque vous testez vos programmes! Commencez par exécuter le serveur et les clients sur la même machine (127.0.0.1).

Le protocole d'identification

  • Lorsqu'un client demande une connexion, il attendra (de façon blocante) que le serveur lui demande de s'identifier.
  • Lorsque le serveur reçoit une demande de connexion, il lui demandera donc de s'identifier (avec une phrase préétablie comme "Identifiez-vous" ou toute autre phrase équivalente de votre choix). Il n'attendra pas une réponse car le serveur ne doit pas bloquer (il y a d'autres clients après tout!).
  • Le client, recevant cette phrase, devra répondre par une autre phrase préétablie et donner son nom (nom qu'il aura préalablement demandé à l'usager). Par exemple: "Mon nom est: Georges". De cette façon, le serveur saura qu'il s'agit d'une identification et ne la traitera pas comme une phrase normale.
  • Le serveur, recevant la phrase préétablie, extraira le nom du client et le stockera dans son SocketPacket.
  • Vous n'avez pas à vous assurer que chaque nom est unique.
  • Bonus: vérifiez si le nom choisi par le client est unique. Si ce n'est pas le cas, le serveur enverra un message préétabli au client, qui redemandera un nom à l'usager.

Les communications

  • Une fois le nom établi, toute phrase entrée par l'usager est envoyée telle quelle au serveur, qui l'expédiera à tous les clients en la précédant du nom du client émetteur, comme ceci: "<Georges> Bonjour les amis". Le client qui a envoyé la phrase la recevra de nouveau, ce qui confirmera à l'usager qu'elle a bel et bien été reçue par le serveur.

Pour le serveur

  • Le serveur doit afficher tout ce qui se passe avec lui de façon à ce qu'un usager qui le démarre soit constamment mis au courant. Ceci sera fort utile pour votre débogage.
  • Le serveur doit demander à l'usager qui le lance d'entrer un port sur lequel écouter. Il utilisera l'adresse IPAddress.Any dans son IPEndPoint afin d'accepter les clients venant de partout.
  • Plutôt que d'utiliser un tableau pour conserver les sockets actifs (comme dans l'exemple fait en classe), vous devriez utiliser une liste générique (System.Collections.Generic.List<>). De cette façon, il est très facile d'ajouter (avec la méthode Add) ou de retirer (avec la méthode Remove) un objet de la liste.
  • Tant qu'à faire une liste générique, faites une List<SocketPacket> (ou toute autre classe similaire qui contient un Socket, un byte[], un numéro d'identification et un nom (choisi par l'usager)). Ainsi, chaque fois qu'un client se connecte (au Socket passif), on créera un SocketPacket contenant son Socket actif (celui avec lequel on communiquera avec le client), un byte[] pour recevoir ses données, un numéro puis de la place pour un nom. Ce SocketPacket sera ajouté à la liste.
  • Il n'est pas nécessaire de réutiliser les numéros assignés aux clients s'ils se déconnectent.
  • Lorsque le serveur reçoit une demande de connexion, il crée le SocketPacket, donne un numéro au client et affiche que le client #x est connecté. Il lui demande ensuite de s'identifier et affiche qu'il le lui a demandé.
  • Lorsque le serveur reçoit le nom, en plus de l'assigner au SocketPacket du client, il affichera le nom qu'il a reçu pour le client #x et il enverra à tous les clients le message "-- NomDuClient s'est connecte" ou "-- AncienNom s'appelle maintenant NouveauNom" (selon s'il reçoit un nom pour la première fois ou non).
  • Lorsqu'un client se déconnecte, le serveur envoie le message "-- NomDuClient s'est deconnecte" à tout le monde et retire le SocketPacket de la liste. On peut aisément détecter une déconnexion puisqu'une exception de type SocketException 10054 est lancée lorsque le Socket est fermé alors qu'on attendait des données en lecture...

Pour le client

  • Le client doit demander à l'usager d'entrer l'adresse et le port du serveur pour s'y connecter.
  • Lorsque le client reçoit une phrase qui n'est pas une demande d'identification du serveur, il l'affiche simplement.
  • Lorsque l'usager entre une phrase, le client l'expédie simplement au serveur.
  • Lorsque l'usager entre "/quit" (ou toute autre phrase préétablie qui ne risque pas d'être utilisée par l'usager dans d'autres circonstances), le client peut simplement terminer (pour ne pas compliquer les choses).

La remise

  • Vous disposez de deux semaines pour terminer ce labo - remettez-le au plus tard le 11 novembre 2009 à 8h55.
  • Envoyez-moi par courriel un .zip de vos projets (assurez-vous de ne pas y laisser trainer d'exécutables, ou de renommer le .zip en .autrechose sinon Gmail refusera votre message).
  • Les périodes de laboratoire du 28 octobre et du 4 novembre seront réservées à ce travail.
  • Vous pouvez faire le travail en équipes de deux.