Page suivante Page précédente Table des matières

2. Méthode de développement

2.1 Conception objet (MVC)

Séparation en modules

La répartition en modules s'est d'abord faite en niveaux logiques. Le fait d'avoir préparé et bien mis à plat la structure du serveur nous a permis de séparer les fonctionnalités du serveur en couches logiques, chacune dépendant de celle du dessous, tout comme le modèle OSI :

\begin{figure}[!hbtp] \begin{center} \epsffile{figures/couche.eps} \caption{Repartition des couches} \end{center} \end{figure}

Nous avons pu dégager quatre modules indépendants de leur couche inférieure :

Couche Bas niveau Sockets

Cette couche regroupe les fonctions de gestion des connexions au niveau des sockets. Cela inclue la gestion des nouvelles connexions, ainsi que celles déjà existantes. Toutes les fonctions de cette couche sont identifiables par le préfixe socket_.

Couche Sécurité

Cette couche, qui est totalement indépendante est appelée par la couche Protocole de communication lorsqu'elle récupère des informations venant de l'extérieur. Par exemple, cette couche se charge de vérifier et, si c'est le cas, de retirer tous les caratères non autorisés, pouvant poser problème. Toutes les fonctions de cette couche sont identifiables par le préfixe secur_.

Couche Gestion Morpion

Cette couche contient les fonctions permettant d'accéder aux données du serveur et permettant de les manipuler. On y trouvera les fonctions de gestion des votes, de test des coups reçus, ou de gestion des parties. Toutes les fonctions de cette couche sont identifiables par le préfixe morpion_

Couche Protocole de communication

Cette couche contient les fonctions relatives à toutes les actions possibles de la part du serveur, en réponse à des demandes du client. Ces fonctions sont toutes indépendantes et représentent chacune un des mots-clef possibles envoyés par un client. Cette couche s'appuye sur la couche Gestion Morpion pour communiquer avec les clients. Toutes les fonctions de cette couche sont identifiables par le préfixe proto_.

Cette organisation en modules a été une expérience intéressante et s'est montrée être d'une très grande utilité lors de la programmation du projet. Le source est facile à modifier et l'ajout de correctifs se fait très rapidement. Le code est robuste car les fonctions sont isolées les unes des autres et sont découpées en fonctions élémentaires. Le code est aussi facilement réutilisable du fait de sont indépendance. Par exemple, on pourra noter la fonction morpion_noticeToAllPlayers() qui est très couramment utilisée dans la plupart des fonctions du protocole. Par exemple, le fait que chaque module puisse être interchangé ou modifié sans que cela ne se répercute sur toutes les fonctions nous a permis de gagner beaucoup de temps.

2.2 Organisation des sources

Le fait d'avoir bien séparé les différentes parties du projet nous a permis de faire de même pour les fichiers sources. Nous les avons séparé par modules indépendants.

Répertoires

.
|-- client
|-- doc
|-- etc
|-- images
|-- include
|-- scripts
|-- server
`-- tests

Tous les fichiers relatif au serveur sont dans le répertoire server, ceux relatif au client sont dans le répertoire client. Les fichier include de déclaration des fonctions sont dans include. Cela inclus les fichiers include du client et du serveur. Le répertoire scripts contient différents scripts servant par exemple au lancement ou à l'arrêt du serveur, à la regénération du fichier contenant les mots-clef (par le programme Gperf). tests contient un programme qui est un mini-client, accompagné d'un script qui permettent de tester automatiquement le serveur avec un nombre important d'utilisateurs fictifs.

Règles de style

Chaque module a ses fonctions identifiées par un mot-clef qui est unique pour un fichier C. Par exemple, toutes les fonctions de gestion bas niveau des communications sont préfixées du mot 'socket':


int socket_print(int sock, char *msg);

Cette méthode de nommage a de nombreux avantages, elle permet de :

2.3 Logiciels utilisés

Autoconf

Autoconf est un programme permettant de tester le système selon certains critères comme la disponibilité de fonctions, la disponibilités de fichiers d'entêtes ou des tests sur la configuration de la machine utilisée pour la compilation. Le but d'autoconf est de pouvoir créer du code portable entre les différents Unix. Ces détections se font par l'intermédiaire de constantes qui sont testées lors de la compilation du programme. Par exemple, la fonction select() n'a pas la même déclaration sur tous les Unix. Celle de Linux est :


int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
                        struct timeval *timeout);

Alors que celle d'une machine sous HP-UX est :


int select (size_t nfds, int *readfds, int *writefds, int *exceptfds,
                        const struct timeval *timeout);

On peut voir que le tableau de descripteur readfds, que nous utilisons n'a pas la même déclaration sur chacun des systèmes. Ce qui génère un warning supplémentaire à la compilation. Grace à autoconf, il nous suffit de tester le système sur lequel on se trouve par l'intermédiaire de #ifdef.

Gperf

Gperf permet de générer une table de hachage ainsi que sa fonction de hachage associée pour que la recherche soit minimale. C'est ce que l'on appelle une table de hachage parfaite. Il n'y a aucune collision. Bien sûr, il n'est pas possible d'ajouter dynamiquement pendant l'exécution du programme des informations. Il faut connaitre à l'avance les mots à placer dans la table. Ce prérequis convient parfaitement au traitement des mots-clef du serveur. Reportez-vous à la section Hachage parfait pour plus d'informations.


Page suivante Page précédente Table des matières