YooGoo

 
 
Bienvenue sur YooGoo
 
 

 
 
News

Description du Projet
Avancement du Projet
L'équipe
Photos du Projet et du reste
 
 

 
 
Téléchargement

Le Client
Le Serveur
Les Sources Serveur
Les Sources Client
Cahiers des Charges
Soutenance 1
Soutenance 2
Soutenance 3
Soutenance Finale
 
 

 
 
Contacts

Franck Tetzlaff CV Tessari Marco CV Bouhelier Stéphane CV Pouiller Jérôme CV
 
 

 
 
Sous-sections

Winsock

Stratégie d'entrées/sorties :

La stratégie d'entrées/sorties est la manière dont les informations reçus et envoyées sont traitées. Un bon choix est déterminant car tout le code réseau repose sur celui ci. Pour rappel les différentes méthodes possibles sont :

Sockets Bloquants
: Par défaut, un socket est bloquant, c'est à dire qu'il ne rend pas la main à l'application tant qu'il n'as pas finis d'effectuer ces tâches ou qu'il y a eu un problème.

Sockets purement non-bloquants
: Un appel avec un socket non-bloquant rend la main immédiatement si il ne réussit pas à terminer sa tâche immédiatement. Ceci permet au programme d'effectuer d'autres tâches pendant que les opérations du réseau finissent. Par contre il faut continuellement tester le socket pour savoir si son opération est finie.

Sockets Asynchrones
: Ce sont des sockets non-bloquants qui renvoient automatiquement un message quand il se passe quelque chose d'intéressant sur le socket.

Select()
: La fonction select() permet à un thread de s'occuper d'un groupe de sockets intéressants, c'est à dire qui reçoivent ou émettent des données. Elle est généralement utilisée pour éviter d'effectuer des test continus sur les sockets non-bloquants.

Objets événement
: utilisés avec WSAEventSelect(), ce mécanisme rassemble à la fonction select(), mais un peu plus efficace. Par contre elle ne fonctionne que sous Windows, tandis que select() fonctionne aussi avec les sockets BSD.

Overlapped I/O
: une des nouvelles fonctions de Winsock 2. C'est la méthode la plus efficace car elle se base directement sur la méthode de Windows pour gérer les entrées/sorties. C'est ce que nous utilisons dabs le serveur YooGoo.

Si on dresse un tableau récapitulatif des différentes méthodes utilisables pour chaque système d'exploitation on obtient :

Win9x WinCE Win NT4+ Win NT 3.x Win16 Unix
Sockets Bloquants oui oui oui oui oui oui
Sockets non-bloquants oui oui oui oui oui oui
Sockets asynchrones oui non oui oui oui non
Objects événement oui non oui non non non
Overlapped I/O oui non oui non non non
Threads oui oui oui oui non oui

Fonctionnement de Winsock

Winsock se présente sous forme d'une dll, il a donc fallu apprendre a insérer une dll dans un projet Visual Studio. En effet, ceci ne se présente pas sous la même forme que sous Delphi ou il suffit d'inclure le .h, il faut utiliser inclure au projet un .lib et le .h correspondant. De plus la librairie Winsock doit être initialisé, ceci se fait par l'appel de la fonction WSAStartup() tandis que la fermeture se faite par WSACleanup() (il est à noter que lors de la fermeture des connections et de la librairie il faut vérifier s'il y a ou nom des messages en attente). Une fois ces opérations réglées on peut accéder à toutes les fonctions externes de la librairies : celles qui sont déclarées dans Winsock.h.

Le choix du non-bloquant

Nou utilisons dans YooGoo des sockets non-bloquants, couplés avec la fonction select() qui permet de savoir s'il y a de l'activité sur un des sockets surveillés. Cette méthode malgré qu 'elle soit plus lente nous permet de ne jamais être bloqué lors d'une réception ou d'un envoie de message. Lorsqu'on envoie (ou reçoit), winsock va faire un appel à la fonction send() (ou recv()) et va envoyer (ou recevoir) le plus de données en un coup, et même si tout le message n'est pas envoyé, il va continuer l'exécution du programme. C'est au programmeur de gérer l'envoie de la fin du message qui n'a pas été envoyé.

Il est important de noter que toutes ces fonctions ont une syntaxe très proche de la version sous Unix des socket, ainsi que la création de thread qui est à peu de choses près pareil. De plus les fonctions utilisant Winsock ont toutes été regroupées dans un même fichier afin de faciliter le portage du code sous une autre plateforme.

La mise en oeuvre

Architecture du serveur

Comme indiqué dans la section architecture, nous avons un thread Winsock pour 64 utilisateurs (voir " Problèmes des sockets non-bloquants. ") Ceci est réalisé grâce à la fonction select(). Pour utiliser la fonction select() il faut créer des ensembles de sockets à surveiller.

Dans chaque thread winsock on retrouve le même fonctionnement. Il y à un tableau statique de 64 pointeurs sur user, le socket qui sert à l'écoute pour savoir s'il y a des connections et trois ensembles de sockets. Un ensemble pour les sockets en écriture (envoie), un pour les sockets en lecture (réception), et un pour les sockets ayant des erreurs. La boucle principale est celle de l'attente de connexions, tout le reste est identique à la version socket bloquant.

Tout d'abord, il faut mettre à jour les ensembles. Le socket d'écoute des connections est mis dans l'ensemble d'erreur et d'écoute (sauf s'il y a déjà 64 utilisateurs gérés dans ce thread). Ensuite on boucle sur tous les utilisateurs gérés dans ce thread. Si l'utilisateur à des messages à envoyer alors son socket est mis dans l'ensemble des sockets en écriture, si l'utilisateur à encore de la place pour recevoir des messages alors son socket est mis dans l'ensemble des sockets en lecture. Tout les sockets sont mis dans l'ensemble des sockets pouvant avoir des erreurs. Cette mise à jour des ensembles est faite à chaque tour de boucle. C'est un des inconvénients de la fonction select().

Ensuite, on appelle la fonction select() avec les trois ensembles précédemment crée. On met un " timeout " à la fonction select(), car si on attend indéfiniment qu'il se passe quelque chose, il se peut qu'il y ait eu des changements dans les utilisateurs, que, par exemple, un utilisateur ai besoin d'envoyer un message, alors que les ensembles n'ont pas été mis à jour, cet utilisateur devra alors attendre qu'il se passe quelque chose dans l'actuelle configuration des ensembles. Ceci pose d'autant plus de problème s'il n'y a qu'un utilisateur connecté. Tout ceci peut se produire uniquement parce qu'il y a un décalage entre le moment où l'on reçoit le message et le moment où le message est traité. On préfère alors rafraîchir les ensembles régulièrement même s'il ne se passe rien. Lorsque la fonction select() attend un événement le thread est mis en pause et donc il n'utilise pas le processeur.

S'il se passe quelque chose sur un socket, la fonction select() renvoie le nombre de sockets en activité. Malheureusement on ne connaît pas le(s) socket(s) qui est(sont) en action. On doit alors vérifier tout les sockets. On commence par le socket en écoute de connections. S'il est actif c'est que quelqu'un essaye de se connecter. On va alors lancer la procédure de connexion. Ensuite on vérifie le socket de chaque utilisateur. S'il est actif en lecture on va appeler la fonction de réception des données, s'il est actif en écriture on va appeler la fonction d'envoie des données. A chaque fois on teste s'il y a des erreurs sur certains sockets, si oui on gère ces erreurs. Si l'erreur est grave (on a perdu la connexion), on va déconnecter l'utilisateur.

Enfin on recommence la boucle. Cette boucle continue jusqu'à l'arrêt du serveur. C'est ce qu'on appelle un thread winsock. Il ne s'occupe donc que de la réception, de l'envoient des messages des utilisateurs et de gérer les connexions et les déconnexions.

Méthode d'envoi et de réception des données

En mode non-bloquant, le programme lance la procédure de lecture ou d'écriture et renvoie tout de suite la main au programme. Il faut donc faire attention car toutes les données n'ont peut-être pas été envoyés, et il faut reprendre plus tard au bon endroit. Pour cela nous avons dans la classe user un " buffer " d'envoie, un " buffer " de réception, le nombre d'octets dans chacun de ces buffers, et la taille du message en cours de réception.

On a découpé la gestion du transfert des messages en trois fonctions.

*
La fonction NetRecv() reçoit les messages qui sont sur le socket tant qu'il y a de la place dans le " buffer " de réception. Il faut à chaque appel de la fonction vérifier à quel point du message on est. On reçoit d'abord les données. Si on a une taille de message à recevoir égale à zéro alors on n'a pas encore commencé à recevoir de messages. Les deux premiers octets sont donc utilisés pour lire la taille du message qui arrive. On supprime ensuite ces deux octets du "buffer". Si le nombre d'octets dans le buffer est supérieur ou égal à la taille du message qu'on est en train de recevoir, on décrypte le message, on l'enfile dans la file de message de l'utilisateur et on enfile l'utilisateur dans la file des utilisateurs qui ont des messages. C'est le thread qui gère les utilisateurs qui traitera ces diverses files. A ce moment on boucle, on regarde si on a deux octets pour avoir la taille du prochain message et on regarde si on a assez d'octets pour recevoir le prochain messages aussi. La fonction se termine dès que le nombre d'octets dans le buffer n'est pas suffisant soit pour lire la taille du prochain message, soit est inférieur à la taille du message que l'on est en train de recevoir. Ce qu'il faut bien comprendre c'est qu'à chaque appel de la fonction on reprend là où l'on s'était arrêté à l'appel précédent, car la fonction peut se terminer dans n'importe quel état.

*
La fonction NetEnqueu() sert à ajouter un message dans le " buffer " d'envoie de l'utilisateur. La fonction s'occupe de crypter le message, et ajoute la taille du message dans le buffer, suivit du message.

*
La fonction NetSend() ne fait rien d'autre que d'envoyer les données qui sont dans le " buffer " d'envoie.

Dans toutes ces fonctions il faut faire attention au débordement de " buffer ". La fonction renvoie le nombre d'octets qui sont soit envoyés, soit reçus, soit enfilés. Si la valeur de retour est zéro c'est qu'il y a eu un problème de "buffer" et il faudra donc réessayer plus tard quand il y aura de la place dans le buffer (ou alors découpé le message en plusieurs morceau si on veut l'envoyé). Si la valeur de retour est -1 c'est qu'il y a eu une erreur de socket et dans ce cas l'erreur du socket sera géré par la boucle principale.

 
 

 
 
Documentation

FAQ
Conception du Protocole
SDK en Ligne
Télécharger SDK
Cahiers des Charges
Soutenance 1
Soutenance 2
Soutenance 3
Soutenance Finale

Comment marche...
Les Canaux
Le Profile
L'administration
La Base de Données
La Cryptographie
Winsock
Le Multithreading
 
 

 
 
Liens

Epita
EpiTarget
Hallucinetik - Zone 42
poupouill.fr.st Minosis (Spé C2)
La Spé C1
La Sup C1
 
 


 
 
made in Epita Powered by ApachePHP Scripting Language

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest © 2000 by YooGoo Team
Des remarques, des question, des choses pas claires? Hargos@ifrance.com