![]() |
||
![]() |
||
![]() |
|
Il est donc important d'utiliser les Unbound thread le plus souvent possible. Les Unbound threads sont les threads créés par défaut. Concepts de la programmation multithreadTout d'abord pour créer un programme multithread, le programmeur doit raisonner en pensant au model multithread sans en abuser. C'est-à-dire qu'il est inutile de mettre des threads partout dans le programme, mais il faut savoir en placer là où il y a nécessité. Un programme doit utiliser les threads pour faire apparaître les activités parallèles à travers :
La librairie des threads est indispensable pour :
Création et gestion des threadsPour pouvoir utiliser les threads dans notre code, il y a un certain nombre de fonctions à savoir utiliser. Il y a la création de thread, la destruction, la mise en pause, la reprise et la synchronisation... Pour ce qui est de la synchronisation, il faut savoir utiliser d'autres objets qu'on nomme objets de synchronisation. Il peut s'agir des mutex ou des sémaphores. Il faut alors savoir les créer et les implémenter là où il faut dans le programme.Création de threadsPour créer un thread sous Windows, il faut appeler la fonction CreateThread qui se trouve dans l'API Windows. Tout ce qui va être dit ne sera vrai que sous le système d'exploitation de Microsoft. En ce qui concerne les autres systèmes d'exploitation, un paragraphe sera consacré au portage du serveur sous Unix. La fonction CreateThread() prend 6 paramètres.
Dans le serveur YooGoo la création de thread intervient au chargement du serveur pour la création des threads canal et user. La partie communication, Winsock, se trouve également dans un thread. Par ailleurs la thread Winsock a la possibilité de créer des threads lorsque le nombre de connectés dépasse un multiple de 64. Notre serveur compte donc au minimum 2 threads. Destruction d'un threadPour sortir du thread en cours, il est possible d'utiliser la fonction ExitThread(), qui ne prend aucun paramètre et qui est l'équivalent d'un break.Mais parfois il est utile de pouvoir terminer un thread sur ordre d'un autre. Par exemple pour l'extinction du serveur, le thread principal donnera lui-même l'ordre de détruire les threads encore actifs. Pour faire cela il utilise la fonction TerminateThread(). Cette fonction est assez simple et ne prend que 2 paramètres :
Dans le serveur la destruction ne s'effectue donc que lorsque le serveur s'éteint ou lorsque le nombre d'utilisateur passe en dessous d'un multiple de 64. Définir la prioritéLa priorité d'un thread est quelque chose de vraiment important car c'est une information qui interagit directement avec le système d'exploitation. Il est possible de voir les différentes priorités attribuées à des programmes à travers le gestionnaire des taches de Windows par exemple. Dans notre projet, il n'a pas encore été pensé de définir une priorité aux threads, mais cela pourrait peut-être changé. Le programme qui est présenté à la soutenance utilise lui le système de priorité.Pour définir une priorité, le plus simple est d'attribuer une priorité à la création du thread car il n'est pas recommandé de changer la priorité d'un thread durant son exécution. Cependant comme le montre le petit programme test, cela est tout à fait possible. Pour cela, il faut utiliser la fonction SetThreadPriority() qui prend les paramètres suivants :
StatistiquesLes threads pourront nous aider en ce qui concerne les statistiques. En effet les threads regroupent un grand nombre d'informations assez importantes aussi bien pour les personnes qui utiliseront YooGoo que pour nous programmeurs. Tout d'abord à l'intérieur du processus, il est possible de savoir pour chaque thread :
Pour obtenir toutes ces informations, il suffit d'appeler la fonction GetThreadTimes() avec les paramètres suivants :
Suspension et reprise d'un threadL'un des objectifs de chaque programme et particulièrement du notre est de faire en sorte que lorsque le serveur n'a rien à faire, il n'utilise pas 100% du processeur, à travers une boucle infinie par exemple. Pour cela il faut utiliser la programmation événementielle. C'est ce que nous avons fait pour le serveur mais également pour le petit client que nous présentons pour cette 2ème soutenance.Tout d'abord avec Visual Studio et C++ Builder, la programmation est événementielle à travers les objets qu'ils proposent. Chaque objet placable que la fenêtre possède un certain nombre d'événements qui lui sont associés. Ainsi lorsqu'on clique sur un bouton de la fenêtre par exemple, cela appelle la fonction associée à l'événement OnClick du bouton. Il faut également savoir que la programmation événementielle n'existe pas seulement avec ces deux logiciels. Il est possible par exemple sous une fenêtre DOS de pouvoir recréer de l'événementiel en analysant les entrées qui sont présentes dans la fenêtre DOS (entrée clavier / souris par exemple). La fonction select dans la thread Winsock fait également la même chose. Elle bloque un certain temps (en fonction du réglage du timer) et n'utilise que très peu de ressources systèmes. Enfin la méthode la plus propre est de suspendre un thread lorsqu'il n'a rien à faire. C'est le cas de la thread de la file des messages. Lorsqu'aucun message n'est présent dans les files de messages des utilisateurs il est inutile de faire tourner la thread en boucle, il faut donc pouvoir la mettre en pause. Mais il faut obligatoirement savoir quand la réveiller. Il faut la reprendre lorsqu'un message est enfilé. A chaque défilage on décrémente un compteur de message. Une fois arrivé à 0, on suspend le thread qui n'occupe plus de ressources au niveau du processeur. Et le thread est réveillé après enfilage. La fonction pour suspendre un thread est SuspendThread(HANDLE hThread) qui retourne -1 en cas d'erreur. La fonction pour reprendre l'exécution d'un thread est ResumeThread(HANDLE hThread) qui retourne -1 en cas d'erreur. SynchronisationLes threads permettent donc de faire des choses qui n'étaient pas possible avant ou difficilement, car l'exécution en séquentielle pouvait bloquer comme c'était le cas dans le client par exemple où la fonction Select figeait l'affichage lorsqu'elle se trouvait dans le thread principal. Cependant il est indispensable de synchroniser les threads entre eux car s'il arrive qu'ils appellent simultanément la même ressource les conséquences peuvent être catastrophiques. Par exemple si une personne modifie un élément de la base de données alors qu'une autre personne essaie de lire la même entrée, le résultat est imprévisible. Il se peut que l'élément renvoyé soit simplement erroné mais il est fort probable que le serveur plante.Pour cela il est nécessaire d'utiliser des objets de synchronisation. Il en existe plusieurs, mais seulement deux nous intéressent, il s'agit du Mutex et du Sémaphore. MutexPar définition un Mutex autorise l'accès mutuellement exclusif, c'est-à-dire qu'il n'est possible qu'à une personne d'accéder à la ressource partagée. Un mutex ne peut appartenir qu'à un seul thread à la fois. Cependant, si un thread possède un mutex et qu'un autre thread en demande la propriété, le thread demandant ne peut l'acquérir que quand le thread propriétaire libère sa propriété.Pour pouvoir utiliser les mutex, la première opération à effectuer est de le créer. Pour cela il faut faire appel à la fonction CreateMutex() qui prend trois paramètres :
Il faut également pouvoir libérer le mutex après la libération de la propriété. Pour cela il faut faire appel à la fonction ReleaseMutex() qui ne prend en paramètre que l'handle du Mutex à libérer. Le Mutex est alors passé au thread en attente s'il y en a. SémaphorePar définition un Sémaphore autorise l'accès à un nombre défini de threads. Un objet Sémaphore démarre avec un compte initial attribué à sa création. Chaque fois qu'un thread devient propriétaire de l'objet (en utilisant la fonction d'attente), le compteur du sémaphore est décrémenté. Chaque fois qu'un thread libère la propriété, le compteur du sémaphore est incrémenté. Si celui-ci est à 0, le sémaphore est bloqué sur l'état non signalé et aucun autre thread ne peut y accéder.De même que pour les Mutex il faut pouvoir créer l'objet sémaphore. Pour cela il faut utiliser la fonction CreateSemaphore() qui avec le même principe que les mutex prend quatre paramètres :
Les sémaphores auraient pu nous permettre de limiter l'accès à la base de données dans l'ancienne structure à un nombre maximum d'utilisateurs. Malheureusement cela n'est plus le cas avec le serveur repensé. Toutefois il est intéressant de remarquer qu'un mutex n'est rien de plus qu'un sémaphore avec un compteur initial à 1 et un maximum de 1. Cette solution va être utilisée pour la compatibilité et le portage sous Unix. De plus c'est la méthode employée dans le petit exemple sur les threads. La fonction de ReleaseSemaphore() doit être appelée pour désigner la libération de la ressource. Elle prend 3 paramètres :
Implémentation des objets de synchronisation dans le programmeAprès avoir créé les objets, il faut pouvoir les appeler juste avant la propriété à protéger. Il faut donc bien veiller à n'oublier aucune zone sensible du programme.Les tests effectués juste avant la propriété sont en fait des appels aux objets de synchronisation. Ces appels sont effectués par la fonction WaitForSingleObject() qui prend 2 paramètres :
Base de donnéesTypes et classes d'objets utilisés dans la base de donnéesA la dernière soutenance, nous avions présenté une méthode de stockage des données selon le principe des AVL (Arbres de recherches équilibrés de Adelson Velskii et Landis). Pour le classement et la recherche d'éléments a occurrence unique, cela suffisait parfaitement.Dans le cas de notre base de données, cela ne suffisait plus et il a fallut modifier notre type abstrait pour qu'il convienne à nos besoins : classer un objet selon plusieurs critères. En effet, nous avions vu la dernière fois que l'objet " user " devait être classé dans plusieurs arbres selon ses différentes caractéristiques. Cet objet etait classe selon son " NickName " (terme anglophone signifiant le surnom) dans l'arbre " sort_by_nick " de la base de données, et respectivement par sa date de naissance, sa ville et son genre dans les arbres " sort_by_birth ", " sort_by_city " et " sort_by_gender ". Ces modifications consistent en un changement du type sur lequel pointent les noeuds des arbres équilibrés : ils pointent sur une liste chaînée d'objets dont les caractéristiques de classement pour cet arbre sont similaires. Par définition, un AVL considère que si un objet d'un noeud a déjà la même valeur que celui inséré, il n'a pas lieu d'être inséré. Pensons par exemple à plusieurs utilisateurs du même âge (mais avec d'autres caractéristiques différentes), qui essaient de créer un compte sur YooGoo et qu'ils ne le peuvent pas tout simplement parce qu'un autre utilisateur du même âge est déjà inscrit. Cette idée est inconcevable pour un logiciel tel que YooGoo. Ce changement est radical et a eu pour conséquence une refonte totale des algorithmes d'ajout, de recherche et de suppression sur les AVL (Rappelons qu'ils restent AVL car ils sont un classement d'utilisateurs selon un critère bien défini et qu'ils restent équilibrés en toute occasion). Le seul problème, cependant, repose sur la suppression d'un utilisateur. Comment supprimer un utilisateur, alors que tous les noeuds à sa première occurrence dans les arbres ont les même caractéristiques. Pour remédier à cela, notre équipe a décidé que les utilisateurs auraient un NickName unique qui les différencierait les uns des autres (évidemment, cela demandera de l'imagination de la part des utilisateurs, mais nous ne doutons pas que cela aura une conséquence positive sur le résultat). Depuis la dernière soutenance, nous avons également rajouté l'arbre des utilisateurs "online" (terme anglophone désignant les utilisateurs en ligne ou encore connectés) totalement gérés par la partie serveur ainsi que l'arbre des canaux. Voici l'aperçu de la classe " database " (terme anglophone signifiant base de données) :
class database {
private:
t_node *sort_by_nick;
t_node *sort_by_gender;
t_node *sort_by_birth;
t_node *sort_by_city;
t_node *sort_by_status;
t_node *channels;
short int updating;
public:
database();
~database();
.
.
.
}
L'entier " updating " sert en particulier à savoir si la base de
données est en activité critique telles que la suppression et
l'insertion. En effet, il ne vaut mieux pas se risquer à lancer
une recherche sur un AVL en pleines modifications (au risque de se
retrouver sur une branche morte un bien une seconde occurrence de
branche). Cette variable n'est qu'une deuxième sécurité ajoutée
aux objets de synchronisation et sera peut-être supprimée par la suite.
Les trois points verticaux représentent les fonctions relatives à la base de données que nous verrons plus tard (Recherches, ajouts et autres suppressions). La fonction database() est la fonction constructive, qui sert donc à construire des objets " database " (elle met tous les arbres à NULL, ils sont vides et elle initialise updating à 0). La fonction database() est la fonction destructrice d'un objet base de données. (Elle vide tous les AVL et les listes chaînées qui les composent). Le type " t_node " désigne le type abstrait des AVL. Voici sa déclaration :
typedef struct s_node {
t_elt *data;
int balance;
struct s_node *lc;
struct s_node *rc;
} t_node;
L'entier " balance " désigne le déséquilibre de l'arbre.
Les composants " lc " et " rc " désignent les sous arbre gauche et sous arbre droit du noeud : ils sont bien évidemment implicitement du type " t_node * " (On peut parler de sous fils droit et sous fils gauche). L'élément " data " de type " t_elt * " et la liste chaînée décrite ci-dessus. Voici la définition du type :
typedef struct s_elt {
void *data;
struct s_elt *nxt;
} t_elt;
L'élément " data " de type " void * " est directement le pointeur
sur la donnée que l'on veut stocker et classer.
Le " nxt " de type implicite " t_elt * " est le pointeur sur l'élément suivant dans la liste chaînée. Modifications des Algorithmes Classiques pour les AVL de YooGooL'insertion d'un élémentL'insertion d'un élément dans un AVL classique fonctionne principalement en insertion en feuille (noeud terminaison d'un arbre). Dans nos AVL, il est possible d'insérer un élément dans un noeud interne.Principe : Soit " A " l'arbre où l'on désire insérer l'élément " X " de type pointeur non typé. Précisons que l'algorithme est récursif est que lorsque l'on parle d'insertion, on relance l'algorithme sur les paramètres indiqués.
Si A est non NUL Alors :
Si l'élément X est strictement inférieur à l'élément de A,
alors on insère X dans le sous fils gauche de A.
Sinon
Si l'élément X est strictement supérieur à l'élément de A alors :
On insère X dans le sous fils droit de A.
Sinon
On est dans le cas où l'élément de A a la même valeur que X.
On insère X dans la liste chaînée du fils courant.
La modification n'aboutit pas à un déséquilibre de l'arbre.
Sinon
On crée un noeud et on insère X dans sa liste chaînée.
Le nouvel élément se trouve donc en feuille.
La modification aboutit à une modification de l'équilibre dans l'arbre.
On fait ce qu'il faut pour rééquilibrer.
Voici l'algorithme dans le langage " Algo " enseigné à
Epita :
NB : les flèches d'affectation sont remplacées par des " = " pour simplifier la lecture et les déférences de pointeurs " " sont remplacées par des " -> ". Les " = " inclus dans les comparaisons des " Si - Alors " sont des fonctions de comparaison et non d'affectation. Les fonctions externes utilisées sont les fonctions classiques des types abstraits sur les listes (surtout " Insérer(Liste l, élément X, Entier place) ") et la fonction passe-partout " compare " qui retourne -1, 0 ou 1 si respectivement l'élément est inférieur, égal ou supérieur à celui comparé (elle est composée d'un grand " Selon les valeurs de () " qui fonctionne selon la variable " type " mise en paramètre). Rappel : Le rééquilibrage des AVL se fait selon les valeurs des " déseq " de chaque noeud et des rotations qui s'ensuivent.
Algorithme fonction at_avl_insert : Entier
Paramètres globaux
AVL A
Paramètres locaux
Elément X
Entier type
Variables
Entier tmp_int
AVL B
Début Si (A <> NUL) Alors
Si (compare(X, ième(A->data, 0), type) = -1) Alors
tmp_int = at_avl_insert(A->fg, X, type)
A.déseq = A.déseq + tmp_int
Si (tmp_int = 0) Alors
Retourne 0
Sinon
Selon les valeurs de (A.déseq)
Cas 0 : Retourne 0
Cas 1 : Retourne 1
Cas 2 :
Si (A->fg.déseq <> -1) Alors
Rotation_GD(A)
Sinon
A.balance = 0
A->fg.balance = 0
Rotation_D(A)
Fin Si
Retourne 0
Fin Selon
Fin Si
Sinon
Si (compare(X, ième(A->data, 0), type) = 1) Alors
tmp_int = at_avl_insert(A->fd, X, type)
A.déseq = A.déseq - tmp_int
Si (tmp_int = 0) Alors
Retourne 0
Sinon
Selon les valeurs de (A.déseq)
Cas 0 : Retourne 0
Cas 1 : Retourne 1
Cas 2 :
Si (A->fd.déseq <> -1) Alors
Rotation_DG(A)
Sinon
A.balance = 0
A->fd.balance = 0
Rotation_G(A)
Fin Si
Retourne 0
Fin Selon
Fin Si
Sinon
Si (compare(X, ième(A->data, 0), type) = 0) Alors
Insérer(A->data, X, 0)
Retourne 0
Fin Si
Fin Si
Fin Si
Sinon
Allouer (B)
B->déseq = 0
B->fg = NUL
B->fd = NUL
B->data = NUL
Insérer(A->data, X, 0) Retourne 1 Fin Si Fin Algorithme fonction
at_avl_insert
Voici les algorithmes des procédures de rotation :
Algorithme Procédure Rotation_D
Paramètres globaux
AVL A
Variables
AVL B
Début
Si ((A <> NUL) et (A->fg <> NUL)) Alors
B = A->fg
A->fg = A->fg->fd
B->fd = A
Si (feuille(A)) Alors
A.déseq = 0
Fin Si
A = B
Fin Si
Fin Algorithme Procédure Rotation_D
Algorithme Procédure Rotation_G
Paramètres globaux
AVL A
Variables
AVL B
Début
Si ((A <> NUL) est (A->fd <> NUL)) Alors
B = A->fd
A->fd = A->fd->fg
B->fg = A
Si (feuille(A) Alors
A.déseq = 0
Fin si
A = B
Fin Si
Fin Algorithme Procédure Rotation_G
Algorithme procédure Rotation_DG
Paramètres globaux
AVL A
Début
Rotation_D(A->fd)
Rotation_G(A)
Fin Algorithme Procédure Rotation_DG
Algorithme procédure Rotation_GD
Paramètres globaux
AVL A
Début
Rotation_G(A->fg)
Rotation_D(A)
Fin Algorithme Procédure Rotation_GD
Suppression d'un élémentC'est certainement la partie qui a demandé le plus de réflexion dans la base de données. En définitive, la suppression marche par adresse. Il faut tout d'abord récupérer l'adresse de l'élément à supprimer pour pouvoir ensuite lancer la suppression sur tous les AVL de la base de données.Principe : L'algorithme se divise en fait en deux parties :
Soit A l'arbre dans lequel on lance la recherche et X soit le pointeur sur élément que l'on veut supprimer soit l'adresse de la liste chaînée du noeud que l'on veut supprimer. (Les deux algorithmes se différencient uniquement par la comparaison : " X " ou " ième(X, 0) ").
Si A est non-nul Alors
Si le premier élément de la liste de A est strictement supérieur à X
(ou supérieur au premier élément de la liste X)
Alors
Lancer la suppression sur le sous fils gauche de A.
Sinon
Si le premier élément de la liste de A est strictement inférieur à X
(ou inférieur au premier élément de la liste X)
Alors
Lancer la suppression sur le sous fils droit de A.
Sinon
Si le premier élément de la liste de A est égal à X
(ou égal au premier élément de la liste X)
Alors
Si A est une feuille Alors
Supprimer X dans la liste de A.
(Supprimer A et rééquilibrer l'arbre.)
Si la liste chaînée de A est vide supprimer A
et rééquilibrer l'arbre.
Sinon
Supprimer X dans la liste de A.
Si la liste de A est vide Alors
Si le déséquilibre de A est -1 Alors
La liste chaînée de A devient le min du
Sous fils droit de A.
On lance la suppression de la liste
Chaînée de A dans son sous-fils droit.
Sinon
La liste chaînée de A devient le Max du
Sous fils gauche de A.
On lance la suppression de la liste
Chaînée de A dans son sous-fils gauche.
Voici les Algorithmes correspondants en Langage " Algo
" :
Première fonction : Celle qui supprime l'élément.
Algorithme Fonction at_avl_delete : Entier
Paramètres globaux
AVL A
Paramètres locaux
Element X
Entier Type
Booleen DelData
Variables
Entier t
Debut
Si (compare(X, ième(A->data, 0)) = -1) Alors
t = at_avl_delete(A->fg, X, deldata)
Si (t = 0)
Retourne 0
Sinon
A.balance = A.balance - t
Retourne at_avl_rebalance(A)
Fin Si
Sinon
Si (compare(X, ième(A->data, 0)) = 1) Alors
t = at_avl_delete(A->fd, X, deldata)
Si (t = 0)
Retourne 0
Sinon
A.balance = A.balance + t
Retourne at_avl_rebalance(A)
Fin Si
Sinon
Si (compare(X, ième(A->data, 0)) = 0) Alors
Si (feuille(A)) Alors
Retourne at_avl_delsel(A, X, DelData)
Sinon
at_avl_delselbis(A, X, Deldata)
Si (estvide(A->data)) Alors
Si (A.balance = -1) Alors
A->data = at_avl_min(A->fd)
t = at_avl_delete2(A->fd, A.data, Type,False)
Si (t = 0) Alors
Retourne 0
Sinon
A.balance = A.balance + t
Retourne at_avl_rebalance(A)
Fin si
Sinon
A->data = at_avl_max(A->fg)
t = at_avl_delete2(A->fg, A.data, Type,False)
Si (t = 0) Alors
Retourne 0
Sinon
A.balance = A.balance - t
Retourne at_avl_rebalance(A)
Fin si
Fin Si
Fin Si
Fin Si
Sinon
Retourne 0
Fin Si
Fin Si
Fin Si
Fin Algorithme Fonction at_avl_delete
Seconde fonction : Celle qui supprime la liste chaînée.
Algorithme Fonction at_avl_delete2 : Entier
Paramètres globaux
AVL A
Paramètres locaux
Liste X
Entier Type
Booleen DelData
Variables
Entier t
Debut
Si (compare(ième(X, 0), ième(A->data, 0)) = -1) Alors
t = at_avl_delete2(A->fg, X, DelData)
Si (t = 0)
Retourne 0
Sinon
A.balance = A.balance - t
Retourne at_avl_rebalance(A)
Fin Si
Sinon
Si (compare(ième(X, 0), ième(A->data, 0)) = 1) Alors
t = at_avl_delete2(A->fd, X, DelData)
Si (t = 0)
Retourne 0
Sinon
A.balance = A.balance + t
Retourne at_avl_rebalance(A)
Fin Si
Sinon
Si (compare(X, ième(A->data, 0)) = 0) Alors
Si (feuille(A)) Alors
Retourne at_avl_delsel(A, X, DelData)
Sinon
at_avl_delselbis(A, X, Deldata)
Si (estvide(A->data)) Alors
Si (A.balance = -1) Alors
A->data = at_avl_min(A->fd)
t = at_avl_delete2(A->fd, A.data, Type,False)
Si (t = 0) Alors
Retourne 0
Sinon
A.balance = A.balance + t
Retourne at_avl_rebalance(A)
Fin si
Sinon
A->data = at_avl_max(A->fg)
t = at_avl_delete2(A->fg, A.data, Type,False)
Si (t = 0) Alors
Retourne 0
Sinon
A.balance = A.balance - t
Retourne at_avl_rebalance(A)
Fin si
Fin Si
Fin Si
Fin Si
Sinon
Retourne 0
Fin Si
Fin Si
Fin Si
Fin Algorithme Fonction at_avl_delete2
Voici l' algorithme de la fonction at_avl_rebalance :
Algorithme Fonction at_avl_rebalace : Entier
Parametres globaux
AVL A
Debut
Selon les valeurs de (A.balance)
Cas -2 :
Selon les valeurs de (A->fd.balance)
Cas -1 :
Rotation_G(A)
Retourne 1
Cas 0 :
Rotation_G(A)
Retourne 0
Cas 2 :
Rotation_DG(A)
Retourne 1
Fin selon
Cas -1 :
Retourne 0
Cas 1 :
Retourne 0
Cas 2 :
Selon les valeurs de (A->fg.balance)
Cas -1 :
Rotation_GD(A)
Retourne 1
Cas 0 :
Rotation_D(A)
Retourne 0
Cas 1 :
Rotation_D(A)
Retourne 0
Fin selon
Fin selon
Fin Algorithme Fonction at_avl_rebalance
Voici les algorithmes de Suppression :
Algorithme Fonction at_avl_delsel : Entier
Paramètre globaux
AVL A
Paramètres locaux
Pointeur data (* pointeur non typé*)
Booleen DelData
Debut
Si (A = NUL) Alors
Retourne 1
Sinon
at_avl_delselbis(A, data, DelData)
Si (estvide(A->data)) Alors
Libèrer(A)
A = NUL
Retourne 1
Sinon
Retourne 0
Fin Si
Fin Si
Fin Algorithme Fonction at_avl_delsel
Algorithmes Procédure at_avl_delselbis
Paramètres globaux
AVL A
Paramètres locaux
Pointeur data
Entier Deldata
Variables
Entier counter
Liste tmp_head
Debut
Si (A <> NUL) Alors
Si (data = A->data) Alors
A->data = NUL
Sinon
tmp_head = A->data
counter = 0
Tant que ((non(vide(tmp_head))) et (ième(tmp_head, 0) <> data)) faire
Tmp_head = tmp_head->nxt
Counter++
Fin tant que
Si ((non((vide(tmp_head))) et (ième(tmp_head, 0) = data)) Alors
SupprimerIème(A->data, counter, DelData)
Fin Si
Fin Si
Fin Algorithme Procédure at_avl_delselbis
Recherche dans un AVL de YooGooLe plutôt simple et répétitif. Cela correspond à une recherche classique dans un arbre binaire de recherche.Principe : En général :
Si le premier élément de la liste de A est supérieur à l'élément X
alors
On relance la recherche sur le sous fils gauche de A.
Sinon
Si le premier élément de la liste de A est inférieur à X alors
On relance la recherche sur le sous fils droit de A.
Sinon
On ajoute à la liste chaînée des résultats, les éléments de la liste
chaînée de A.
Si on recherche un Utilisateur ou un Canal bien précis, par
exemple, on retourne directement le pointeur sur le premier
élément de la liste chaînée de A (vu que chaque Canal et
Utilisateur ont chacun un nom unique, A ne peut qu'avoir un
élément par liste chaînée) : ce principe sera utilisé lorsque l'on
recherchera à supprimer un élément ; On récupère le pointeur que
l'on met en paramètre de la fonction que l'on a rencontrée
précédemment.
Par contre, ci on recherche l'ensemble des Utilisateurs ou Canaux qui ont leurs noms qui commencent comme l'élément X, on utilisera plutôt ce principe d'algorithme ci-dessous : (on utilisera une fonction de comparaison et d'inclusion de liste chaînée)
Si le premier élément de la liste de A est supérieur à l'élément X
alors
On relance la recherche sur le sous fils gauche de A
Sinon
Si le premier élément de la liste de A est supérieur
à l'élément X alors
On relance la recherche sur le sous fils droit de A
Sinon
Si le premier élément de la liste de A commence par X
(X inclus dans A) alors
Ajouter les éléments de la liste chaînée de A dans la
liste chaînée des résultats.
Si le sous fils gauche de A est non nul et si le premier
élément de sa liste commence par X alors
Poursuivre la recherche sur le sous fils gauche de A.
Si le sous fils droit de A est non nul et si le premier
élément de sa liste commence par X alors
Poursuivre la recherche sur le sous fils droit de A.
Fonction avancées de la base de donnéesDans cette partie, nous allons décrire les différentes fonctions élaborées selon les besoins du server de YooGoo.
Organisation de la base de donnéesVoici comment s'organise la base de données dans ses différents fichiers sources :
Réalisation du protocoleLe parseurUn parseur est une fonction qui permet de divisé une ligne de commande en plusieurs arguments. Généralement le premier argument est le nom de la fonction, tandis que les autres sont ses paramètres. On parle alors de parser un message, afin d'en obtenir les différents arguments qui vont devoir être exploités. Cette définition était nécessaire car le terme parser est employé plusieurs fois au long du rapport.Lorsque le client ou le serveur reçoit un message, celui ci est une chaîne ascii. Il faut alors pouvoir interpréter ce message, pour cela on va traité ces arguments un à un après l'avoir parsé. Le parseur prend donc en argument une chaîne ascii, et renvoie un tableau d'arguments. Le nombre d'argument de ce tableau est limité à 100 et chaque argument ne peut faire plus de 255 caractères. Ces nombres ont un peu exagéré vu qu'on ne peu recevoir de message de plus de 512 octets. On renvoie aussi le nombre d'arguments dans le tableau. La fonction parseur se limite à recopier dans le tableau d'arguments la chaîne donnée en argument jusqu'à un espace ou alors s'il y a eu précédemment des guillemets ouvrants, jusqu'à des guillemets fermant. En effet, les seuls délimiteurs possibles sont ' ' et ' "'. Ensuite elle incrémente le nombre d'arguments et continue le même traitement pour l'argument suivant, jusqu'à la fin de la chaîne. Le protocoleUne fois que l'on a parsé le message, le tableau d'argument obtenu est envoyé à la fonction protocole. Celle-ci effectue une suite de comparaisons de chaînes sur l'argument n°1 pour savoir à quelle commande il correspond. Ensuite, en fonction de la commande, il vérifie le nombre d'arguments. Puis, il vérifie si les users et les canaux mis en arguments existent et récupère leurs pointeurs en faisant appel à la base de données. Puis, pour les fonctions d'administration des canaux, il vérifie les droits de l'utilisateur. Enfin, il effectue divers petits tests en fonction des commandes appelées.L'implémentation du protocole a été largement simplifié par la conception préliminaire de proto.h. En regardant les valeurs retournés par la fonction, il est facile de voir les tests qui doivent être fait. Le protocole renvoie ensuite un numéro d'erreur au client. 000 indique que tout s'est bien passe. Les numéros inférieurs à 100 indiquent que la commande a été effectuée mais qu'un fait notable s'est produit. Cela arrive si vous voulez par exemple joindre un canal sur lequel vous etes déjà connecté. Les valeurs supérieures à 400 indiquent une erreur. Lorsque le client envoie une commande au serveur, celui-ci lui envoie forcement un unique numéro d'erreur. Le client reçoit aussi les évènements du serveur. Par exemple, lorsqu'un utilisateur se joint au canal, les autres connectés reçoivent le message : join <channel> <nick> pour les informer de l'évènement. Là aussi, les messages serveur -> client ont été définis au préalable dans proto.h. Néanmoins, un message tel que celui là est assez peu parlant pour un utilisateur. Il faut que son client le parse et lui envoie plutôt un message genre : <Nick> s'est join au canal <channel>. Ou bien, nous pourrions envoyer directement le message sous sa forme "human-readable". Ceci entraînerait des problèmes pour parser le message et des problèmes pour gérer les différentes langues. Nous gardons donc pour le moment la première solution. Le protocole recevant des arguments de l'extérieur, il doit aussi être capable de gérer les erreurs de l'utilisateur. Nous avons donc fait beaucoup de debugging. Nous testons surtout la taille des buffers et des chaînes. A ce jour, toutes fonctions 'simples' ont été implémentées. Cela comprend la gestion du profil et la gestion des canaux. Les fonctions évoluées qui demandent l'autorisation d'autres utilisateurs ou bien qui renvoient des informations dont la taille ne permet pas de les envoyer en une seule fois ne sont pas encore gérées. La classe de logDans le monde Unix il est commun d'avoir une trace de ce qui se passe sur un serveur. C'est pourquoi nous avons intégré à YooGoo une classe de log qui permet de logger dans un fichier diverses informations sur l'activité du serveur.Après l'installation du serveur YooGoo, les logs se trouveront dans le répertoire " ./logs ". Il y a 3 fichiers de log différents. Un pour le log des connexions et des déconnections, un pour l'activité de la base de données, et un pour le debug. Dans les fichiers on à une information par ligne. Pour exploiter ces fichiers sous Windows il faudra des outils spécialisés dans le travail sur les fichiers, par contre sous Unix on peut exploiter ces fichiers facilement grâce à des outils tel que " grep, tail, cut, wc ...". Il y a un fichier par jour. Ainsi si le serveur YooGoo tourne plus de 24h d'affilé, à 0h00 sont créé des nouveaux fichiers de logs. Ceci facilite les statistiques par jour et permet de ne pas avoir des fichiers trop volumineux. Les différents niveauxIl y a différent niveau de log choisit par l'utilisateur. C'est une combinaison de flags. Les différents flags sont :
Les différentes fonctionsPour écrire une ligne dans un fichier de log il suffit d'utiliser les fonctions :
// pour tout ce qui concerne les utilisateurs et les canaux
int Clog::Add_Log(int lvl, const char * msg);
//pour tout ce qui concerne la base de données
int Clog ::Add_Db(int lvl, const char * msg);
// pour le debug et les commentaires
int Clog::Add_Msg(int lvl, const char * msg);
avec lvl une combinaison des différents modes. Selon la
fonction choisit on écrira dans un fichier ou un autre. De plus
certains fichiers ne sont pas crées si le niveau de log ne le
nécessite pas.
Deux méthodes différentes étaient possible quand à la saisie des messages à écrire dans les fichiers de logs. Soit on faisait beaucoup de fonctions écrivant des messages prédéterminés, ceci permettait de pouvoir gérer dans la classe de log même les différentes langues, et avoir une certaine harmonie entre les différentes entrées. Soit celui qui appelle la fonctions de log est libre de saisir ce qu'il veux. C'est la deuxième solution qui a été retenue. La seule chose qui est commune à toutes les lignes, est la date et l'heure au debut de la ligne, sous forme " [AAAAMMJJ HH:MM:SS] " . La gestions de plusieurs langues est donc remise à plus tard. Portage sous Unix?Le portage du serveur sous Unix n'est pas prévu pour cette soutenance, mais il a déjà été pensé. Les seules divergences qui peuvent apparaître entre les deux systèmes d'exploitation viennent de la gestion des Sockets et de la gestion des threads.En ce qui concerne la gestion des threads nous utilisons une classe qui permet la compatibilité entre les 2 OS. Cette différence intervient surtout dans l'utilisation des mutex car la gestion des threads est presque semblable. La classe utilise des sémaphores assimilables à des Mutex pour la compatibilité. Pour ce qui est des Sockets, nous nous sommes déjà renseigné et les Sockets de BSD ne diffèrent pas tant que ça avec WinSock étant donné que Microsoft s'est inspiré (une fois de plus) des sockets de BSD. En attendant la suiteLe serveur presque près a permis à certains membres de s'occuper de tout ce qui devrait faire la réussite de YooGoo : le site web et le client. Ces deux éléments ne sont pas à négliger car ce sera ce que les utilisateurs verront le plus. Pour préparer le terrain, les outils de création d'interface ont été testés afin de choisir celui qui nous sera le plus profitable. Ainsi un gros travail de préparation sur le futur client a été fait. En effet, le client risque de nous prendre plus de temps que prévu donc nous essayons de prendre de l'avance. De plus un client basique pour pouvoir utiliser les nombreuses fonctionnalités du serveur a été fait. En ce qui concerne le site web, il semblerait que déjà pas mal de monde soit attiré par YooGoo, et que notre projet est convoitisé par d'autres car nous avons été victime d'un hack. De plus nous avons voulu bien préparé la soutenance et avons donc pensé à une présentation graphique et structurée avec PowerPoint.Travail sur le clientMFC (Visual Studio)AvantagesAfin de développer un premier client graphique, nous nous sommes penché dans un premier temps sur le logiciel qui nous a permis de créer notre serveur, à savoir Visual Studio 6. Le logiciel de Microsoft utilise 2 technologies pour l'interface graphique de ses projets. La première est sans doute la moins connue, c'est la technique ATL (Active Template Library) dont on ne parlera pas. La deuxième la plus connue est la technologie MFC (Microsoft Foundary Library). Dans un premier temps les MFC semblent être très pratiques et très puissantes. On dispose d'une large palette de contrôles à disposés dans la fenêtre de dialogue. De plus nous avons la possibilité d'intégrer des ActiveX disponibles un peu partout. La création graphique des pages est vraiment intuitive. De plus dans la version supérieure de Visual Studio (le .Net) les propriétés des objets sont modifiables beaucoup plus aisément.InconvénientsMais les inconvénients sont importants. Tout d'abord le lien des objets au code du projet est totalement différent de ce que proposait Microsoft avec Visual Basic. Pour pouvoir utilisé un contrôle dans notre code il faut passer obligatoirement par une variable intermédiaire. Celle-ci est obtenue par des appels au ClassWizard, un complément de Visual Studio permettant de gérer les objets (classes) de la fenêtre plus simplement. Seulement l'héritage des classes a été bâclé. Ainsi pour faire des fonctions de base comme griser un bouton à partir du code est un véritable calvaire. Pour faire cela il faut que notre variable m_textbox par exemple lié au contrôle textbox soit utilisée de la manière suivante : m_textbox.SetWindowEnable(False).On ne voit aucun rapport entre l'appellation Window dans la fonction et notre contrôle textbox. Le vice va encore plus loin dans la gestion des événements. Il a été jusqu'à présent impossible de gérer l'événement OnKeyUp qui réagit à chaque relâchement de touche à l'intérieur d'un textbox. Cet événement n'a marché que pour une fenêtre de dialogue vierge ce qui n'a aucun intérêt. Nous avons bien essayé de résoudre tous les problèmes rencontrés mais il est impossibles d'obtenir des cours potables sur les MFC sur le net. Les seuls résultats de recherche obtenue étaient pour des cours sur une semaine au prix exorbitant de 2500 frs / jour. De plus l'intégration de fichier codé purement en C ou C++ tel que le serveur YooGoo n'a pas été une partie de plaisir. En effet lors de la première compilation, le compilateur nous a sorti des erreurs que nous ne supposions pas pouvoir exister. Par exemple une erreur était du genre " fichier trop court ... ". Le problème vient du fait que les projets MFC de Visual C++ utilisent des en-têtes précompilées. C'est-à-dire que le compilateur exécute préalablement les instructions situées dans les en-têtes avant même d'avoir ouvert le corps de notre code en C. Pour pouvoir stopper ce problème il faut modifier des propriétés des units du projet (désactiver l'option en-têtes précompilées dans le menu settings). De plus la compilation avec Visual .Net sort tous les jours de nouvelles erreurs de link qui disparaissent et réapparaissent lorsqu'elles le veulent. Toutes ces petites choses font que Visual C++ ne sera pas utilisé pour la conception du client et nous utiliserons un logiciel certes moins puissant mais beaucoup moins capricieux comme C++ Builder. C++ Builder (API Windows)AvantagesAvec C++ Builder, l'interface graphique est la même que sous Delphi. La prise en main a donc été très rapide. Le principe est le même que pour Visual. On dispose d'un certain nombre de contrôles, qui se placent dans la fenêtre à créer. La différence vient de la gestion de ces objets. Contrairement à Visual, dans la fenêtre des propriétés des objets, il existe un onglet regroupant tous les événements gérés par le contrôle. Et ces événements marchent, de même que pour la modification de propriétés à l'intérieur du code. Ce qui a pris 2 jours avec Visual a mis 15 secondes avec C++ Builder. D'un point de vue global, C++ Builder est assez paramétrable mais moins convivial d'un point de vue de code pur. De plus l'intégration du code de l'ancien client (code C++ pur) n'a posé aucun problème.Du côté de l'interface du code Borland est assez personnalisable. On peut paramétrer les couleurs des instructions, des strings ... Et pour se rapprocher de Visual il est possible d'utiliser les mêmes raccourcis clavier pour la compilation. Certes l'indentation et la mise en forme sont bien moins performantes que sous Visual mais Borland C++ Builder a l'énorme avantage de ne pas être gourmand en mémoire. Il est alors possible de débuguer le client et le serveur en même temps. InconvénientsTout d'abord, il faut souligner que le compilateur de Borland est moins performant que celui de Visual dans plusieurs domaines. D'une part il est incapable de recompiler un programme en cours de debuging contrairement à son rival. Par ailleurs les fenêtres de debugging sont compliquées à gérer. D'un point de vue visuel C++ Builder n'est pas très beau et on comprend mal pourquoi Borland a décider d'en faire un programme multi-fenêtres ou les autres programmes Windows apparaissent en tâches de fond...De plus C++ Builder a été fortement déconseillé par Krisboul mais nous n'avons toujours pas vu pourquoi. Aucun problème n'a pour l'instant été constaté. Client Graphique(Version de base)Pour cette 2ème soutenance nous voulions présenter un serveur opérationnel pour avoir une bonne avance sur nos prévisions. Et pour tester le serveur nous devions disposer d'un client potable. Pour cela nous avons décidé de créer un premier client graphique avec les fonctionnalités de bases.Contrôles de la fenêtreNotre petit client est un programme monofenêtre qui est en fait une boite de dialogue. Celle-ci se décompose en deux parties.La première est une partie dédiée à la connexion au serveur. Elle est composée de :
Fonctions implémentéesLes fonctions implémentées ne vont pas très loin. Il ne s'agit que des fonctions de base. Elles regroupent les fonctions déjà disponibles dans le client texte remis à jour et quelque nouveautés :
Passage en non-bloquantPour pouvoir utiliser notre client avec la nouvelle version du serveur, il a fallu faire quelque modification, notamment dans la manière de communiquer. Effectivement, le serveur fonctionne désormais en non-bloquant, il semble donc évident que le client doit être basé sur le même principe. Pour passer le client en non-bloquant, nous avons déjà passé le client texte en non-bloquant mais il était très difficile de le tester ainsi. Le code a donc été implémenté dans le client. Il a alors fallu travailler sur la fonction select de winsock qui surveille l'activité sur les sockets.La fonction select prend dans son dernier paramètre une valeur qui détermine son mode de fonctionnement. Ce paramètre désigne en quelque sorte le temps d'attente du select avant de continuer l'exécution de la boucle. Une valeur 0 correspond à un mode bloquant, ici le 0 équivaut en fait à une valeur infinie. Dans notre cas, il a fallu recréer un timer du type Winsock. Ce timer est une structure regroupant un champs pour les secondes et un autre pour les millisecondes. Pour le client nous avons choisi une valeur très courte : 1 ms. Dès lors, le client fonctionnait en mode non-bloquant. Utilisation de ThreadLe passage en mode non-bloquant a causé une remise en question de la structure de notre premier client graphique, car une fois entrée dans la boucle de communication le reste du programme perdait la main.Nous avons donc eu recours à l'exécution en parallèle. Nous avons donc créé un thread supplémentaire dédié exclusivement aux communication entrantes et sortantes. Ce thread est créé lors de la connexion au serveur. Avec ce thread, le reste du programme continue de tourner normalement, et il devient possible de cliquer sur des contrôles de la fenêtre. On comprend alors tout l'intérêt des threads. Par ailleurs, la déconnexion a également du être repensée. En effet celle-ci est totalement différente que l'on soit en bloquant ou non-bloquant. Pour effectuer une déconnexion proprement il faut s'assurer que les buffers d'émission et de réception soient bien vides. Or avec deux threads il est impossible d'en être sûr à 100%. Nous avons donc créer une variable de déconnexion qui prends l'état 1 lorsqu'on clique sur le bouton déconnexion ou lorsqu'on détruit la fenêtre. Cette variable agit directement sur le thread de communication qu'elle conditionne. Lorsqu'elle est à 1 la boucle de communication vide les buffers s'il y a lieu d'être sinon elle quitte directement la boucle et détruit le thread proprement. Il ne reste alors plus qu'à détruire le socket correspondant et la déconnexion s'est effectuée proprement. Le site webLe Site web n'a pas subit de grande refonte. On peut noté un petit changement dans la galerie photos : celle-ci permet de gérer des vidéos maintenant. Comme nous l'avions prévu, l'entretient est assez simple grace aux scripts PHP. Le travail a surtout consisté à entretenir les nouvelles versions en téléchargement, la galerie photo et les news.Le piratage du site webIl y a eu un fait notable sur le site web le 24 Janvier à 18:40. Alors que Hargos allait faire l'entretient quotidien du site web, il a vu que la première page avait été complètement modifiée. Il a tout de suite compris ce qui venait de se passer. Un pirate venait de passer par-là. L'interface était complètement modifiée. Elle redirigeait au bout de quelques secondes vers www.disney.com. A la place du logo habituel, il y avait une photo de Lvg (aussi appelé Jésus). Il y avait de grande chance que se soit lui qui ai fait le coup. Il faut prendre des décisions rapidement. Tout d'abord avertir le reste du groupe. Ensuite, on a redirigé www.yooGoo.com vers le serveur de Danao qui avait par chance une version du site à peu près à jour. Nous avons ensuite vérifié que le mot de passe du site n'avait pas été modifié. Par chance non. Nous avons donc regardé s'il y avait eu des modifications dans la base SQL. En cherchant un peu, nous avons trouvé une news un peu spéciale: elle contenait du java script. En effet, sur la page d'index, le script recherche les 10 dernières news dans la base SQL et les affiches. En mettant du Java script dans les news, le pirate à redirigé le site vers www.disney.com. Nous avons ensuite regardé les fichiers du site pour voir si aucun n'avait été modifié. Il y en avait effectivement. Nous avons donc remplacé les fichiers qui avaient été modifiés pendant la journée par ceux que nous avions en sauvegarde sur nos disques. Le site était redevenu normal. Nous avons remis le lien www.YooGoo.com vers yoogoo.multimania.com. Reste à savoir qui? quand? comment? Jésus a revendiqué le hack de notre site dès le lendemain (aucune surprise). Il nous dit avoir mis en place le hack du site vers 18:30 ce qui concorde avec la date de modifications des fichiers. Il a utilisé une faille de download.php lui permettant de télécharger n'importe quels fichiers du site. Il a ainsi pu étudier le source PHP du site en détail. Il a ensuite découvert une faille dans counter.php (un script permettant de savoir la fréquentation du site). Celle-ci lui permettait d'envoyer une n'importe quelle requête SQL. Grâce à ça, il a pu modifier la base SQL et ajouter la news qui contenait du Java script. A partir de là, il a uploadé des fichiers sur le serveur web. Il a donc changé notre logo et l'interface du site. Nous avons réparé les failles du site. Le script counter.php ne nous servait plus depuis quelques temps déjà puisque nous utilisons Xiti. Le script counter.php a donc été supprimé.ImpactsXiti nous a permis de voir l'impact du piratage du site. A 18:50 le site a été redirigé et il paraissait saint à toutes les personnes qui y allaient. Le site est donc resté seulement 20min dans sa version piratée. Xiti nous informe qu'il y a eu seulement 5 visites entre 18h et 19h. Toujours d'après Xiti, la redirection chez Danao a permit à 15 visiteurs de ne pas se retrouver sur la page hackée. Mais surtout, on remarque 1349 pages vues 102 visites et 72 visiteurs. C'est le tiers de la fréquentation mensuelle en une journée. Effectivement, quand Jésus a piraté le site, il a averti beaucoup de monde. Le bouche à oreille aidant, il a fait de la pub qui se ressent encore une semaine plus tard (voir évolution du mois de janvier). Bilan du piratage du site : quasiment personne n'a vu le site piraté et on enregistre une augmentation de 400% du nombre de visites par rapport au mois de décembre. L'impact de ce hack est donc très positif.Présentation de la soutenance (PowerPoint)Pour les soutenances à venir nous avons décidé de présenter notre projet de manière plus professionnel et plus attrayante. Pour cela nous avons donc utiliser un programme destiné principalement aux professionnels dans le but de créer des présentations graphiques. Il s'agit du logiciel PowerPoint de Microsoft.DescriptionCe logiciel permet de créer assez rapidement des présentations graphiques avec possibilité d'animation. Il est possible de créer plusieurs types d'organigrammes, du standard aux plus sophistiqués. Ce logiciel permet en plus d'ajouter quelques séquences sonores qui sont liées à des événements.Tous les programmes que nous voulons montrer sur le PC principal seront exécutés à partir de PowerPoint. Il en est de même pour le site. PowerPoint possède en plus quelques fonctions pense-bête dans le but d'éviter les trous de mémoire. Il est d'ailleurs possible d'ajouter une voix narrateur pour commenter toute la soutenance mais cela n'a pas beaucoup d'intérêt dans notre cas. Cependant ceci pourra être utile pour expliquer le fonctionnement du logiciel Client YooGoo dans l'aide qui sera fournie avec le CD d'installation. ExplicationsLe contenu des pages qui seront présentées sous PowerPoint regroupera toutes les idées de notre plan de soutenance. Le contenu est pour l'instant assez flou car le plan n'a pas encore été décidé.PowerPoint pourra nous aider à améliorer la clarté de notre soutenance, d'appuyer nos explications de planifier, à temporiser notre soutenance (réglage de l'intervalle de temps entre deux pages) et à l'accélérer car lorsqu'on passe par du visuel on gagne toujours du temps (ex : regarder un match à la télé et l'écouter à la radio !!!) ConclusionL'opinion de chacunDanaoTout d'abord j'ai appris différentes choses totalement nouvelles dans plusieurs domaines. Je me suis familiarisé avec les threads et comprends presque totalement leur fonctionnement (j'ai lu de grandes parties de différents livres sur ce sujet, plus des tutoriaux et des cours sur le net). J'ai également appris pourquoi les MFC découragent une grande partie de leurs utilisateurs et comment faire sans elles. Par conséquent je connais un autre logiciel de programmation, C++ Builder.Je me suis également plongé dans la programmation événementielle que ce soit en mode graphique ou en mode texte (en mode console DOS). De plus j'ai fait quelque chose qui me tenait à coeur, redonner un peu de vie à la console DOS en y mettant des couleurs et en recodant une fonction gotoxy. En travaillant avec Marco, j'ai amélioré ma compréhension de Winsock et compris tout le code qui se trouve dans la partie client. Enfin c'est la première fois que j'attache autant d'importance à la préparation d'une soutenance et à mener à bien le projet jusqu'à son terme. FolkenLa base de données de YooGoo est terminée pour la seconde soutenance. A l'origine, elle devait l'être pour la troisième soutenance. En bref, cela est dû aux commandes des autres membres du projet qui en avaient besoins dans leurs parties. Au final, j'ai conservé mon avance depuis la dernière soutenance.Le développement de cette base de données m'a permis d'apprendre à faire évoluer un algorithme en fonction de certains besoins. Cela m'a permis de rendre mon code plus évolutif et d'être efficace pour répondre plus rapidement aux besoins des autres membres du groupe de projet. J'ai également appris à être plus rigoureux dans mon code en m'imposant une grande partie de la norme EPITA (déclarations et syntaxes) et en compilant ma partie avec différents environnements (Visual Studio et GCC). Cela abouti à une grande portabilité du code de la base de données. HargosPour cette soutenance, pas de mauvaises surprises. Nous avions fait assez attention à ce que les codes des différentes parties soient compatibles. Contrairement à ce que j'aurais cru, la fusion des parties s'est bien déroulée. A part quelques petites fonctions qui n'avaient pas été codées comme tout le monde le souhaitait, dans l'ensemble, nos codes étaient compatibles.La conception du protocole pour la première soutenance nous a beaucoup aidé. L'implémentation du protocole a été relativement simple. En regardant les valeurs retournées, il est facile de voir l'algorithme à utiliser. Avec le protocole sous la main, j'ai codé sans avoir besoin de beaucoup réfléchir (ça va vite et ça fatigue pas trop). De même, l'entretien du site web est devenu très automatisé grâce aux scripts PHP. Nous remarquons maintenant que le temps que nous avions pris pour préparer l'architecture de YooGoo porte ses fruits. ZapataCette soutenance, contrairement à la dernière, a été pour moi moins riche en nouveautés. Le C/C++ devient mon langage naturel, les objets deviennent des outils familiers. D'un point de vue algorithmique, cette soutenance ne m'a pas apportée grand chose. De plus mon travail a été très ciblé : il s'agissait de finir le serveur pour cette soutenance. Mission accomplie!Mais tout n'est pas si simple. J'ai surtout appris l'importance de la phase de conception d'un projet. En effet notre architecture du serveur a dû être modifiée à cause d'une conception trop rapide, et surtout d'une mauvaise connaissance des outils à utiliser lors de la phase de conception. Ainsi j'ai dû me replongé dans Winsock, et dans des fonctions plus avancées que celles étudiées précédemment. Afin de compléter mes connaissances réseaux (et Unix), un portage sous Unix serait intéressant. Un autre point à ne pas négliger est la répartition des tâches, car si celle ci est mal faite, certain membre du groupe risque d'être retardé par les autres. Un autre point important de cette soutenance était le travail d'équipe. Il a fallu regrouper le code de chacun. On constate alors une grande différence de style de programmation, mais au final si la conception est bien faite, cette différence n'est pas importante car tout s'emboîte à merveille. Pour finir, je dois dire que cette expérience annuelle de projet libre est pour moi capitale dans mes études car nous acquerons une grande expérience de conduite de projet qui pour moi se révèle capitale pour notre future vie professionnelle. Le mot de la finD'après le planning que nous nous étions fixé, le serveur est tout à fait dans les temps, voire même en avance. Il ne reste plus qu'à l'améliorer pour la prochaine soutenance. Une ou deux personnes seulement vont continuer à travailler dessus. Là où nous avons pris une grande avance c'est sur le client. Grande avance sur le planning peut être, mais il reste encore énormément de choses à faire et c'est toujours le temps qui manque. Le reste du groupe va donc travailler sur le client afin de pouvoir exploiter toutes les fonctionnalités du serveur et peut être pouvoir commencé à le distribuer sur Internet afin de tester YooGoo en grandeur nature. | ![]() |
|
|