Activité

Mise en situation

On désire coder une application Qt graphique d’analyse d’adresse IPv4 qui doit renseigner sur un certain nombre d’informations sur le réseau auquel elle appartient. Cette application doit donc assurer la même fonction que la commande ipcalc de Linux ou un équivalent en ligne comme IP Calculator link.

Une proposition d'interface homme-machine (abrégée IHM par la suite) pour cette application figure ci-dessous :

QNetCalc

Adressage IPv4

L’application Qt demandée étant destinée à analyser une adresse IPv4, il est nécessaire de bien comprendre la structure de cette adresse et comment elle est utilisée au sein d’un réseau.

Vous allez donc devoir vous renseigner sur ces points.

🖮 Travail n° 1 Appropriation de l’adressage IPv4

  1. Se renseigner sur internet sur l’adressage IPv4 et la notion de “sous-réseau” (masque, adresse réseau, adresse de broadcast, nombre d’hôtes, plage d’adresses des hôtes).

    Ci-dessous, une liste de ressources qui constituent un bon point de départ :

    • Sous réseau (Wikipedia) link

    • l’excellent article de Philippe Latu sur l’adressage IPv4 disponible sur le site www.inet.doc link

      Ce dernier document évoque les classes d’adresses IP nommés A, B, C, D et E. Comme il est d’ailleurs bien précisé dans l’article, cette technique de regroupement n’est plus utilisée de nos jours et a été remplacée par le routage inter-domaine sans classe (abrégé CIDR en anglais pour Classles Inter-Domain Routing). Cependant, il n’est pas rare de la rencontrer dans des article un peu anciens.

      En résumé, il faut bannir cette notion de classe d’adresses de son vocabulaire sous peine de passer pour un has been 😉.

  2. Répondre aux question suivantes :

    1. Notation CIDR

      Soit l’adresse 192.168.80.16/20

      • Combien de bits sont utilisés pour identifier la partie réseau ?

      • Combien de bits sont utilisés pour identifier la partie hôte ?

    2. Adresses d’un réseau :

      Soit le réseau 192.168.80.0/20

      • Combien d’hôtes ce réseau peut-il au maximum contenir ?

      • Donner la plage d’adresses attribuables aux hôtes de ce réseau

      • Donner l’adresse de diffusion (broadcast) de ce réseau

    3. Découpage d’un réseau en sous-réseaux

      Soit le réseau 192.168.80.0/20 que l’on veut découper en sous-réseaux.

      Il faut définir un masque réseau étendu qui permette de placer un certain nombre d’hôtes dans chaque sous-réseau.

      • Combien de bits sont nécessaires sur la partie hôte de l’adresse réseau pour accueillir au moins 500 hôtes ?

      • Quel est alors le nombre maximum d’adresses d’hôte utilisables dans chaque sous-réseau ?

      • Combien de bits restent-ils pour coder les sous-réseaux ?

      • Quel est alors le nombre maximum de sous-réseaux qu’il est possible de définir ?

      • Quelle est la plage d’adresses attribuables aux hôtes du 2ème sous-réseau ?

🖮 Travail n° 2 Calcul d’adresses IPv4 en C/C++

Soit l’adresse IPv4 172.16.52.27/20 (soit 1010 1100 0001 0000 0011 0100 0001 1011 en binaire ou 0xac10341b en hexadécimal) stockée dans une variable C ipv4 déclarée comme suit :

quint32 ipv4 = 0xac10341b;

Le type de variable quint32 est un “alias” fournit par Qt pour désigner un unsigned int donc un entier sur 32bits non signé.

  1. Après avoir consulté les documents ressources Opérateurs bit à bit et Techniques de masquage en C/C++, donner les expressions en langage C qui permettent d’obtenir :

    1. l’identifiant de l’hôte dans le réseau

    2. l’adresse réseau

    3. l’adresse de broadcast

      • Pour extraire l’identifiant d’un hôte dans le réseau depuis une adresse IPv4, il suffit de faire un ET bit à bit entre le masque réseau et l’adresse IP de cet hôte.

      • Pour obtenir l’adresse réseau d’un réseau IPv4, il suffit de remplir de 0 la partie de l’adresse réservée à l’identifiant d’hôte.

      • Pour obtenir l’adresse de broadcast d’un réseau IPv4, il suffit de remplir de 1 la partie de l’adresse réservée à l’identifiant d’hôte.

Codage de l’application Qt d’analyse d’adresse IPv4

Vous allez à présent créer dans Qt Creator le projet qui va rassembler toutes les informations et fichiers qui composeront l’application Qt demandée.

De façon à bien structurer le code, l’application sera codée dans 3 fichiers .cpp principaux :

  1. main.cpp : Point d’entrée du programme. Il a été généré automatiquement par l’IDE lorsqu’on a créé l’application. On ne le modifie que très rarement.

  2. mainihm.cpp : Code de gestion de l’interface graphique décrite par le fichier mainihm.ui. Ce fichier est partiellement généré lorsqu’on crée une nouvelle form (fenêtre, boîte de dialogue…​). Il est à compléter pour gérer les interactions avec l’utilisateur.

  3. ip.cpp : Code créé manuellement pour traiter toutes les opérations liées à une adresse IP (détermination du réseau auquel elle appartient, de la valeur du masque,…​). C’est donc la classe métier qui est au cœur du problème à traiter. Les fonctions qu’il contient sont appelées depuis le fichier mainihm.cpp.

🖮 Travail n° 3 Création du projet et conception de l’IHM

  1. Lancer Qt Creator

  2. Créer un nouveau projet de type Application  Qt Widgets Application qui utilisera comme classe de base QDialog voire QMainWindow si vous désirez par la suite que votre application dispose de menus, barre d’état…​

    Nommer cette classe MainIHM

  3. S’inspirer de l’IHM proposée en début de sujet pour concevoir celle de votre application à partir de l’outil Qt Designer qui s’ouvre lorsqu’on double-clique sur le fichier avec l’extension .ui (→ mainihm.ui) qui se trouve dans le dossier Forms dans l'explorateur de projets (à gauche de la zone d’édition par défaut).

    ihm qtcreator
Consignes pour la conception de l’IHM
  • L’IHM doit permettre de saisir une adresse IP en notation décimale pointée avec son masque exprimé sous le format CIDR (Ex. : 192.168.1.5/24).

  • Des champs éditables (→ line edit) seront utilisés pour les informations à saisir (4 champs pour l’adresse IP saisie en notation décimale pointée + 1 champ pour la longueur du masque)

  • Ne pas garder les noms attribués par défaut aux différents éléments graphiques (ou widgets) de l’IHM. Les nommer de façon à y accéder sans ambiguité depuis les programmes (ex. : btnAnalyser plutôt que pushButton_2 pour le bouton “Analyser” présent sur l’IHM proposée).

    objectname
  • Utiliser les composants layouts et spacers pour arranger/aligner les différents widgets de l’IHM

    layouts spacers

▸ Classe MainIHM (début)

La classe MainIHM gère l’interface graphique et, dans ce cadre, elle :

  • traite les évènements utilisateur ( saisie dans les champs, appui sur le bouton “Analyser”)

  • contrôle la saisie de l’utilisateur

  • met à jour à l’affichage

🖮 Travail n° 4 Gestion de l’évènement généré par le bouton “Analyser”

De façon à traiter l’appui sur le bouton “Analyser”, le constructeur de la classe MainIHM doit mettre en place une connexion entre le signal émis par le bouton lorsqu’on clique dessus et le slot — c’est-à-dire la méthode à exécuter lorsque le signal est émis — qui lui est associé. Le codage du slot est de la responsabilité du programmeur (vous).

La déclaration du slot peut se faire de manière graphique depuis Qt Designer mais, dans un 1er temps, il est préférable de la faire manuellement dans le programme de façon à bien comprendre le mécanisme des signaux et des slots. (→ par exemple onBtnAnalyseClicked() définie par l’utilisateur dans le fichier mainihm.cpp).

  1. Ouvrir le fichier mainihm.h et déclarer un slot pour le signal du bouton “Analyser”.

    Ceci se code en ajoutant les lignes suivantes au sein de la déclaration de la classe MainIHM :

    public slots :
        void onBtnAnalyserClicked();
    • public slots initie une section (comme private ou public) dans laquelle on déclare les slots. Ce n’est pas du C++ standard mais sera interprété par l’outil moc de Qt pour générer, cette fois-ci, du C++ standard avant de lancer la compilation proprement dîte.

    • Le nom utilisé pour le slot importe peu mais il est d’usage de le préfixer de on suivi du nom de l’objet qui génère le signal et du nom du signal lui-même.

    • Le slot peut dans certains cas posséder des paramètres (quand le signal qui lui est connecté en possède lui-même).

  2. Effectuer la connexion du signal au slot. Pour cela, aller dans le code du constructeur de la la classe MainIHM dans le fichier mainihml.cpp et appeler la méthode connect() du framework Qt dont le rôle est justement de réaliser cette connexion :

    connect(this->ui->btnAnalyser,SIGNAL(clicked()), this, SLOT(onBtnAnalyserClicked()));

    Pour obtenir de l’aide sur une méthode de Qt, il suffit de placer le curseur dessus dans l’éditeur de texte et enfoncer la touche F1.

  3. Coder une ébauche de slot pour vérifier le bon fonctionnement. Nous allons nous contenter d’afficher une boite de dialogue modale prédéfinie qui s’affichera dès que le bouton est appuyé.

    • En s’aidant d’internet, expliquer ce qu’est une fenêtre modale.

    • Inclure dans mainihm.cpp le fichier d’entête <QMessageBox> qui permet d’utiliser par la suite les boîtes de dialogue modal

    • Ajouter le code du slot dans le fichier mainihm.cpp :

      void MainIHM::onBtnAnalyserClicked() {
         QMessageBox::information(this, "Notification", "Le bouton `Analyser` a été enfoncé");
      }
  4. Exécuter l’application pour vérifier que la boite de dialogue modale s’affiche effectivement lorsque le bouton est enfoncé.

  5. Parcourir la documentation de Qt et donner les 4 types de boîtes de dialogue modale prédéfinies dans Qt.

🖮 Travail n° 5 Récupération et contrôle des informations saisies

Lorsque le bouton “Analyser” est enfoncé il faut récupérer les différents constituants de l’adresse IPv4 saisie et la longueur de son masque afin de pouvoir en tirer les différentes informations demandées (adresse réseau, masque, nombre d’hôtes …​).

Au lieu de considérer l’adresse IPv4 comme 4 entiers distincts, on va les regrouper dans un tableau. Qt propose un type de tableau — nommé QByteArray qui est bien adapté au stockage d’octets et qui offre bien plus de fonctionnalités que les tableaux traditionnels du langage C.

  1. Inclure dans le fichier mainihm.cpp le fichier d’entête <QByteArray> requis dès lors qu’on utilise des variables de type QByteArray

  2. Dans le slot du bouton “Analyser”, déclarer une variable ipValue de type QByteArray

  3. Toujours dans le slot, stocker dans le tableau les 4 octets de l’adresse IPv4 saisie.

    Le code pour récupérer, sous forme décimale (et non de texte), une de ces valeurs est :

    bool ok;
    ipValue[ 0 ] = this->ui->ledtIP0->text().toInt(&ok);
    • ipValue[ 0 ] désigne le 1er élément du tableau (celui d’indice 0)

    • this→ui→ledtIP0→text() récupère la chaîne de caractères saisie dans le widget nommé ledtIP0 qui contient l’octet de poids fort de l’adresse IP.

    • .toInt(&ok) convertit en nombre entier la chaîne de caractères récupérée. Suite à l’appel, la variable ok contiendra true si la conversion a réussi et false dans le cas contraire (si on saisi par exemple une valeur qui ne représente pas un nombre).

  4. Compléter le slot pour afficher dans une boîte de dialogue modale un message — c’est-à-dire une chaîne de caractères — qui informe sur l’adresse IP saisie.

    Construire le message à partir des éléments du tableau de façon à expérimenter la technique qui permet de passer d’un nombre à une chaîne de caractères. Un code d’exemple vous est proposé :

    QByteArray octets;
    octets[0] = 0xc0;
    QString msg = "Le 1er octet du tableau vaut : " + QString::number((unsigned char)octets[0]);
  5. Vérifier le bon fonctionnement du code en saisissant l’adresse 192.168.2.254.

  6. Supprimer le (unsigned char) dans chacun des appels à QString::number() puis exécuter le code en saisissant la même adresse IPv4 (→ 192.168.2.254).

    Expliquer le résultat obtenu.

  7. Déclarer dans le slot une nouvelle variable de type int nommée ipMaskLen et y stocker la longueur du masque saisie après l’avoir convertie en nombre entier.

  8. Faire évoluer le code du slot de façon à ce que celui-ci vérifie les valeurs saisies (une valeur comprise entre 0 et 255 pour les constituants de l’adresse IP et une valeur comprise entre 0 et 32 pour la longueur du masque).

    Le code affichera un message d’erreur (→ boite de dialogue modale) à la 1ère valeur non valide rencontrée, effacera ensuite le champ de saisie correspondant dans l’IHM (→ ex. : this→ui→ledtIP0→clear()) puis le sélectionnera en plaçant le curseur dessus (→ ex. : this→ui→ledtIP0→setFocus();).

▸ Classe IP

Comme évoqué dans la présentation de la structure du code, l’extraction ou calcul des informations liées à l’adresse IPv4 saisie sera effectué au sein d’une classe métier dédiée dont la déclaration vous est donnée ci-dessous accompagnée de commentaires qui expliquent le rôle des différents attributs et méthodes :

Fichier ip.h
#ifndef IP_H
#define IP_H

#include <QByteArray>

class IP
{
private:
    quint32 mIPv4; // Adresse IP "binaire" depuis laquelle les informations
                  // doivent être extraites/calculées
    int mMaskLen;  // Longueur du masque saisie
    quint32 mask; // Masque "binaire" correspondant à la longueur
                  // du masque saisie dans l'IHM
    quint32 mNetwork; // Adresse réseau calculée
    quint32 mBroadcast; // Adresse de broadcast calculée

private:
    // Convertit une IP ou masque binaire en tableau d'octets
    // en vue d'un affichage en noatation décimale pointée
    QByteArray convertToByteArray(quint32 val);

public:
    // Constructeur
    IP();
    // Calcule les différentes informations liées à une adresse IPv4
    // en notation CIDR (A.B.C.D/Mask)
    void setAddress(QByteArray theIP, int theMaskLen);
    // Getter pour permettre de récupérer dans l'IHM le masque
    // sous forme de 4 octets
    QByteArray getMask();
    // Getter pour permettre de récupérer dans l'IHM l'adresse réseau
    // sous forme de 4 octets
    QByteArray getNetwork();
    // Getter pour permettre de récupérer dans l'IHM l'adresse de broadcast
    // sous forme de 4 octets
    QByteArray getBroadcast();
    // Getter pour permettre de récupérer dans l'IHM l'adresse du 1er hôte
    // sous forme de 4 octets
    QByteArray getFirstHost();
    // Getter pour permettre de récupérer dans l'IHM l'adresse du dernier hôte
    // sous forme de 4 octets
    QByteArray getLastHost();
    // Getter pour permettre de récupérer le nombre max. d'hôte dans le
    // réseau considéré
    int getMaxNbHosts();

};

#endif // IP_H

Une convention de nommage largement répandue pour les attributs d’une classe est de les préfixer par un m ou m_ pour “membre” (Ex. : mNetwork ou m_network).

Vous allez à présent coder les différentes méthodes de la classe IP

🖮 Travail n° 6 Définition initiale de la classe IP

  1. Ajouter une nouvelle classe nommée IP dans le projet.

  2. Compléter la déclaration de la classe IP dans le fichier ip.h avec ce qui vous a été fourni plus haut.

  3. Donner une implémentation “vide” provisoire des différentes méthodes de la classe IP dans le fichier ip.cpp pour éviter les erreurs de compilations.

    IP::IP() { }
    
    IP::~IP() { }
    
    void IP::setAddress(QByteArray theIP, int theMaskLen) { return; }
    
    QByteArray IP::convertToByteArray(quint32 val) { return QByteArray(); }
    
    QByteArray IP::getMask() { return QByteArray(); }
    
    QByteArray IP::getNetwork() { return QByteArray(); }
    
    QByteArray IP::getBroadcast() { return QByteArray(); }
    
    QByteArray IP::getFirstHost() { return QByteArray(); }
    
    QByteArray IP::getLastHost() { return QByteArray(); }
    
    int IP::getMaxNbHosts() { return 0; }
  4. Compléter le constructeur pour qu’il initialise à 0 tous les attributs de la classe

🖮 Travail n° 7 Codage de la méthode setAddress()

Dans la classe IP, la méthode setAddress() joue un rôle essentiel puisqu’elle consiste à :

  • mémoriser sous forme d’entiers non signés sur 32 bits (→ quint32) l’adresse IP fournie en paramètre en tant que tableau d’octets (→ QByteArray) et le masque réseau à partir de sa longueur fournie également en paramètre

  • déduire, par calcul sur cette adresse au format binaire, les autres informations (adresse réseau, adresse de broadcast)

Vous allez à présent coder cette méthode.

  1. Débuter le codage de la méthode en mémorisant dans l’attribut mMaskLen le paramètre theMaskLen

  2. En s’appuyant sur les opérateurs bit à bit vus dans le document ressource Opérateurs bit à bit en C/C++, poursuivre le codage de la méthode en stockant dans l’attribut mIPv4 l’adresse IP fournie en paramètre en tant que tableau d’octets.

    mipv4
  3. Elaborer dans l’attribut mMask le masque binaire sur 32 bits à partir de la longueur du masque (valeur accessible soit depuis l’attribut mMaskLen ou le paramètre theMaskLen).

    La technique consiste à forcer à 0 les (32 - mMaskLen) bits les plus à droite d’une valeur de 32 bits totalement remplie de 1 au départ

  4. Calculer à présent l’adresse réseau, à mémoriser dans l’attribut mNetwork en effectuant l’opération bit à bit appropriée entre l’adresse IP (→ mIPv4) et le masque réseau (→ mMask)

  5. Coder enfin le calcul de l’adresse de broadcast, à stocker dans l’attribut mBroadcast, en se rappelant qu’elle consiste à remplir de 1 l’identifiant de l’hôte dans le réseau.

🖮 Travail n° 8 Codage de la méthode convertToByteArray()

Le plus gros du travail est fait !! Les informations essentielles ont été extraites de l’adresse IP fournie par l’utilisateur au travers de l’IHM.

Il s’agit à présent de fournir le code des méthodes qui permettront à l’IHM de récupérer toutes les informations pour leur affichage soit directement depuis les attributs mMask, mNetwork, mBroadcast calculés dans la méthode setAddress() soit après un calcul arithmétique basique sur les informations à notre disposition.

L’IHM a été conçue pour afficher les différentes adresses réseau intéressantes en notation décimale pointée. Or, dans la classe IP, celles-ci sont codées dans un entier sur 32 bits. On va donc coder une méthode utilitaire — nommée convertToByteArray() dans la déclaration de la classe — qui va permettre de convertir une adresse codée sur 32 bits en tableau de 4 octets.

  1. Coder la méthode convertToByteArray() pour répartir le contenu d’un entier de 32 bits dans 4 valeurs de 8 bits.

    QByteArray IP::convertToByteArray(quint32 val) {
       QByteArray ba;
    
       // Extraire successivement 4 x 8 bits de la valeur de `val`
       // et les stocker dans le tableau `ba`
    
       return ba;
    }
    converttobytearray

    On pourra se référer à la section Opérations sur les champs de bits qui explique le principe à appliquer dans cette méthode.


Il suffit à présent de coder les méthodes get…​(), à l’exception de getMaxNbHosts(), de façon à ce qu’elles retournent sous forme de tableau de 4 octets les informations auxquelles elles sont rattachées.

Ces méthodes qui permettent de récupérer depuis l’extérieur de la classe des attributs privés à celle-ci sont appelés des getters (ou accesseurs en français). Une convention largement adoptée pour les nommer est de les préfixer avec get suivi du nom de l’attribut que la méthode doit retourner.

🖮 Travail n° 9 Codage des getters

  1. Coder les méthodes getMask(), getNetwork(), getBroadcast() pour qu’elles retournent respectivement les attributs mMask, mNetwork, mBroadcast après leur conversion en tableau de 4 octets.

  2. Coder les méthodes getFirstHost() et getLastHost() pour qu’elle retournent sous forme de tableau de 4 octets la 1ère et la dernière adresse disponible pour un hôte sachant que ces adresses correspondant respectivement à l’adresse réseau + 1 et à l’adresse de broadcast - 1

🖮 Travail n° 10 Codage de la méthode getMaxNbHosts()

La dernière méthode à coder est getMaxNbHosts(). Elle doit retourner le nombre maximum d’hôtes que peut contenir le réseau considéré.

Ce nombre peut être calculé de différentes manières mais on considère qu’on souhaite le calculer avec la formule suivante : nbMaxHosts = 232-mMaskLen - 2

  1. Coder la méthode getMaxNbHosts() pour qu’elle calcule et retourne le nombre maximum d’hôtes dans le réseau considéré à partir de la formule donnée.

    La fonction “puissance” s’appelle qPow() dans Qt. Elle est déclarée dans le fichier d’entête <qmath.h>.

▸ Classe MainIHM (suite et fin)

Jusqu’à présent, vous avez codé dans le slot du bouton “Analyser” les instructions qui permettent de lire depuis l’IHM l’adresse IPv4 saisie par l’utilisateur. Vous avez ensuite codé la classe IP qui permet de calculer et rendre disponible les différentes informations qui peuvent être extraites de l’adresse IP saisie. Il ne vous reste donc plus qu’à mettre à jour l’affichage avec ces informations.

Cet affichage doit également prendre place dans le slot puisqu’il doit être réalisé dès que l’utilisateur appuie sur le bouton “Analyser”.

🖮 Travail n° 11 Affichage des informations extraites de l’adresse IP saisie

  1. Créer un nouvel attribut mUserIP de type ÌP dans le fichier mainihm.h sans oublier d’inclure le fichier d’entête ip.h

  2. Poursuivre le codage du slot onBtnAnalyseClicked() en invoquant la méthode setAdress() sur l’objet mUserIP de façon à lancer le calcul des différentes informations relatives à l’adresse IP saisie par l’utilisateur.

  3. Appeler ensuite les différents getters de l’objet mUserIP pour récupérer ces informations puis les afficher dans leurs champs respectifs de l’IHM après leur conversion en chaîne de caractères (→ QString::number((unsigned char)info))

  4. Tester l’application finale.

Pour ceux qui auraient fini…​

🖮 Travail n° 12 Evolution de l’application

  1. Enrichir l’IHM en proposant un bouton qui permet d’effacer l’ensemble des champs éditables.

  2. Enrichir l’application pour qu’elle indique si l’adresse saisie est de type privée ou publique.

🞄  🞄  🞄