Comment communique-t-on en réseau ?

Nous allons donc voir les notions théoriques de base sur le réseau de façon light et ludique. 

Pour nos exemples, nous allons imaginer 2 utilisateurs. Appelons-les... par exemple Patrice et Ludovic.
Patrice et Ludovic ont chacun un ordinateur et ils voudraient communiquer entre eux.

Communication en réseau

Comment faire ? Comment communiquer, sachant qu'il y a des centaines, des milliers d'autres ordinateurs sur le réseau ?
Et comment peuvent-ils se faire comprendre entre eux, faut-il qu'ils parlent le même langage ?

Pour que vous puissiez avoir 2 programmes qui communiquent entre eux via le réseau, il vous faut 3 choses :

  1. Connaître l'adresse IP identifiant l'autre ordinateur.
  2. Utiliser un port libre et ouvert.
  3. Utiliser le même protocole de transmission des données.

Si tous ces éléments sont réunis, c'est bon.
Voyons voir comment faire pour avoir tout ça...

1/ L'adresse IP : identification des machines sur le réseau

La première chose qui devrait vous préoccuper, c'est de savoir comment les ordinateurs font pour se reconnaître entre eux sur un réseau.
Comment fait Patrice pour envoyer un message à Ludovic et seulement à lui ?

Qu'est-ce qu'une IP ?

Il faut savoir que chaque ordinateur est identifié sur le réseau par ce qu'on appelle une adresse IP. C'est une série de nombres, par exemple :

85.215.27.118

Cette adresse représente un ordinateur. Lorsque vous connaissez l'adresse IP de la personne avec qui vous voulez communiquer, vous savez déjà au moins vers qui vous vous dirigez.

Mais voilà, le problème, parce que sinon ça serait trop simple, c'est qu'un ordinateur peut avoir non pas une mais plusieurs IP.
En général aujourd'hui, on peut considérer qu'un ordinateur a en moyenne 3 IP :

  • Une IP interne : c'est le localhost, aussi appelé loopback. C'est une IP qui sert pour communiquer à soi-même. Pas très utile vu qu'on n'emprunte pas le réseau du coup, mais ça nous sera très pratique pour les tests vous verrez.
    Exemple : 127.0.0.1
  • Une IP du réseau local : si vous avez plusieurs ordinateurs en réseau chez vous, ils peuvent communiquer entre eux sans passer par internet grâce à ces IP. Elles sont propres au réseau de votre maison.
    Exemple : 192.168.0.3
  • Une IP internet : c'est l'IP utilisée pour communiquer avec tous les autres ordinateurs de la planète qui sont connectés à internet.
    Exemple : 86.79.12.105

Patrice et Ludovic ont donc plusieurs IP, selon le niveau auquel on se place :

Si je vous raconte ça, c'est parce que nous aurons besoin d'utiliser l'une ou l'autre de ces IP en fonction de la distance qui sépare Patrice de Ludovic.

Si Patrice et Ludovic sont dans une même maison, reliés par un réseau local, nous utiliserons une IP du réseau local (en rouge sur mon schéma).
Si Patrice et Ludovic sont reliés par internet, nous utiliserons leur adresse internet (en vert).

Pour ce qui est de l'adresse localhost, elle peut nous servir pour "simuler" le fonctionnement du réseau. Si Patrice envoie un message à 127.0.0.1, celui-ci va immédiatement lui revenir. 

Retrouver son adresse IP

Comment je connais mon IP ? Ou plutôt mes IP ?
Et comment je sais laquelle correspond au réseau local, et laquelle correspond à celle d'internet ?

La méthode dépend de l'IP que vous recherchez.

  • Pour l'IP interne : pas besoin d'aller chercher plus loin, à coup sûr c'est 127.0.0.1 (ou son équivalent texte "localhost").
  • Pour l'IP locale : pour la retrouver tout dépend de votre système d'exploitation.
    • Sous Windows, ouvrez une invite de commande (par exemple celui que vous utilisez avec Qt pour compiler) et tapezipconfig
      Il est possible que vous ayez plusieurs réponses, en fonction des moyens de connexion disponibles (câble ethernet, wifi...). En tout cas, l'une des IP que l'on vous donne est la bonne (à la ligne "Adresse IPv4").
    • Sous Linux ou Mac OS, c'est le même principe dans une console mais pas la même commande :ifconfig
      L'adresse est en général de la forme "192.168.XXX.XXX", mais cela peut être parfois différent.
  • Pour l'IP internet : le plus simple est probablement d'aller sur un site web qui est capable de vous la donner, comme par exemple www.whatismyip.com !

Maintenant que vous connaissez l'adresse IP de votre interlocuteur, alors vous allez pouvoir communiquer avec lui... ou presque. Le problème, c'est qu'il y a plusieurs portes d'entrée sur chaque ordinateur. C'est ce qu'on appelle les ports.

2/ Les ports : différents moyens d'accès à un même ordinateur

Un ordinateur connecté à un réseau reçoit beaucoup de messages en même temps.
Par exemple, si vous allez sur un site web en même temps que vous récupérez vos mails, des données différentes vont vous arriver simultanément.

Pour ne pas confondre ces données et organiser tout ce bazar, on a inventé le concept de port.
Un port est un nombre compris entre 0 et 65 535. Voici quelques ports célèbres :

  • 21 : utilisé par les logiciels FTP pour envoyer et recevoir des fichiers.
  • 80 : utilisé pour naviguer sur le web par votre navigateur (par exemple Firefox, ou plutôt zNavigo ).
  • 110 : utilisé pour la réception de mails.

Imaginez que ces ports sont autant de portes d'entrée à votre ordinateur :

Ports

Si on veut faire un programme qui communique avec Ludovic, il va falloir choisir un port qui ne soit pas déjà utilisé par un autre programme.

La plupart des ports dont les numéros sont inférieurs à 1 024 sont déjà réservés par votre machine. Nous ferons donc en sorte de préférence dans notre programme d'utiliser un numéro de port compris entre 1 024 et 65 535.

Pour éviter que n'importe quel programme puisse communiquer sur le réseau et accéder à une machine sans autorisation, on a inventé les firewalls (pare-feu). Leur rôle est de bloquer tous les ports d'une machine, et d'en autoriser seulement certains qui sont considérés comme "sûrs" (comme les ports 21, 80, 110...).
Il faudra bien vérifier la configuration de votre firewall si vous en avez un (il y en a un activé sous Windows par défaut depuis Windows XP SP2), car celui-ci pourrait tout simplement bloquer les communications de notre programme !

3/ Le protocole : transmettre des données avec le même "langage"

Bon, nous savons désormais 2 choses :

  • Chaque ordinateur est identifié par une adresse IP.
  • On peut accéder à une IP via des milliers de ports différents.

L'IP, vous savez la retrouver. Le port, il faudra en choisir un qui soit libre (nous verrons comment en pratique plus tard).
Vous êtes donc maintenant en mesure d'établir une connexion avec un ordinateur distant, car vous avez les 2 éléments nécessaires : une IP et un port.

Il reste maintenant à envoyer des données à l'ordinateur distant pour que les 2 programmes puissent "parler" entre eux. Et ça mine de rien, ce n'est pas simple. En effet, il faut que les 2 programmes parlent la même langue, le même protocole. Il faut qu'ils communiquent de la même façon.

Définition : un protocole est un ensemble de règles qui permettent à 2 ordinateurs de communiquer. Il faut impérativement que les 2 ordinateurs parlent le même protocole pour que l'échange de données puisse fonctionner.

Exemple de la vie courante : vous dites "Bonjour" lorsque vous commencez à parler à quelqu'un, et "Au revoir" lorsque vous partez. Eh bien pour les ordinateurs c'est pareil !

Les différents niveaux des protocoles de communication

Il existe des centaines de protocoles de communication différents. Ceux-ci peuvent être très simples comme très complexes, selon si vous discutez à un "haut niveau" ou à un "bas niveau". On peut donc les ranger dans 2 catégories :

  • Protocoles de haut niveau : par exemple le protocole FTP, qui utilise le port 21 pour envoyer et recevoir des fichiers, est un système d'échange de données de haut niveau. Son mode de fonctionnement est déjà écrit et documenté. Il est donc assez facile à utiliser, mais on ne peut pas lui rajouter des possibilités.
  • Protocoles de bas niveau : par exemple le protocole TCP. Il est utilisé par les programmes pour lesquels aucun protocole de haut niveau ne convient. Vous devrez manipuler les données qui transitent sur le réseau octet par octet. C'est plus difficile, mais vous pouvez faire tout ce que vous voulez.

Protocoles

Pour ceux qui veulent aller plus loin, renseignez-vous sur le modèle OSI. C'est un modèle d'organisation des données sur le réseau qui vous explique les différents niveaux de communication (on parle de "couches").

Les protocoles de haut niveau utilisent des ports bien connus et déjà définis.
Les protocoles de bas niveau peuvent emprunter n'importe quel port, sont beaucoup plus flexibles, mais le problème c'est qu'il faut définir tout leur fonctionnement.

En fait, tous les protocoles de haut niveau utilisent des protocoles de bas niveau pour leur fonctionnement interne.
Les protocoles de bas niveau sont "la base", on les utilise pour construire des protocoles de plus haut niveau.

Nous n'allons pas créer un logiciel de mails, ni un client FTP. Nous allons inventer notre propre technique de discussion pour notre programme, notre propre protocole basé sur un protocole de bas niveau... Nous allons donc travailler à bas niveau.

Les protocoles de bas niveau TCP et UDP

Il faut savoir que les données s'envoient sur le réseau par petits bouts. On parle de paquets, qui peuvent être chacun découpés en sous-paquets :

Par exemple, imaginons que Patrice envoie à Ludovic le message : "Salut Ludovic, comment ça va ?". Le message ne sera peut-être pas envoyé d'un seul coup, il sera probablement découpé en plus petits paquets. Par exemple, on peut imaginer qu'il y aura 4 sous-paquets (j'invente, car le découpage sera peut-être différent) :

  1. Sous-paquet 1 : "Salut Ludov"
  2. Sous-paquet 2 : "ic, co"
  3. Sous-paquet 3 : "mment ça v"
  4. Sous-paquet 4 : "a ?"

Ce n'est pas vous qui gérez le découpage en sous-paquets, c'est le protocole de bas niveau qui s'en occupe. Il est donc impossible de connaître à l'avance la taille des paquets ou même leur nombre.
Il est cependant important de savoir que ça fonctionne comme ça pour la suite.

On peut envoyer ces paquets de plusieurs façons différentes, tout dépend du protocole de bas niveau que l'on utilise :

  • Protocole TCP : le plus classique. Il nécessite d'établir une connexion au préalable entre les ordinateurs. Il y a un système de contrôle qui permet de demander à renvoyer un paquet au cas où l'un d'entre eux se serait perdu sur le réseau (ça arrive ). Par conséquent, avec TCP on est sûr que tous les paquets arrivent à destination, et dans le bon ordre.
    En contrepartie de ces contrôles sécurisants, l'envoi des données est plus lent qu'avec UDP.
  • Protocole UDP : il ne nécessite pas d'établir de connexion au préalable et il est très rapide. En revanche, il n'y a aucun contrôle ce qui fait qu'un paquet de données peut très bien se perdre sans qu'on en soit informé, ou les paquets peuvent arriver dans le désordre !

Il va falloir choisir l'un de ces 2 protocoles.
Pour moi, le choix est tout fait : ce sera TCP. En effet, nous allons réaliser un Chat et nous ne pouvons pas nous permettre que des messages (ou des bouts de messages) n'arrivent pas à destination, sinon la conversation pourrait devenir difficile à suivre et on risquerait de recevoir des messages comme : "Salut Ludovmment ça va ?"

Mais alors, du coup tout le monde utilise TCP pour être sûr que le paquet arrive à destination non ? Qui peut bien être assez fou pour utiliser UDP ?

Certaines applications complexes qui utilisent beaucoup le réseau peuvent être amenées à utiliser UDP. Je pense par exemple aux jeux vidéo.

Prenez un jeu de stratégie comme Starcraft, ou un FPS comme Quake par exemple : il peut y avoir des dizaines d'unités qui se déplacent sur la carte en même temps. Il faut en continu envoyer la nouvelle position des unités qui se déplacent à tous les ordinateurs de la partie. On a donc besoin d'un protocole rapide comme UDP, et si un paquet se perd ce n'est pas grave : vu que la position des joueurs est rafraîchie plusieurs fois par seconde, ça ne se verra pas.

L'architecture du projet de Chat avec Qt

Nous venons de voir quelques petites notions théoriques sur le réseau, mais il va encore falloir préciser quelle est l'architecture réseau de notre programme de Chat.

Une architecture réseau ? Qu'est-ce que c'est que ça ?

Jusqu'ici, nous avons supposé un cas très simple : il n'y avait que 2 ordinateurs (celui de Patrice et celui de Ludovic). Le problème, c'est que notre programme de Chat doit permettre à plus de 2 personnes de discuter en même temps. Imaginons qu'une troisième personne appelée Vincent arrive sur le Chat. Vous le placez où sur le schéma ? Au milieu entre les 2 autres compères ?

Les architectures réseau

Pour faire simple, on a 2 architectures possibles pour résoudre le problème :

  • Architecture client / serveur

Une architecture client / serveur : c'est l'architecture réseau la plus classique et la plus simple à mettre en oeuvre. Les machines des utilisateurs (Patrice, Ludovic, Vincent...) sont appelées des "clients". En plus de ces machines, on utilise un autre ordinateur (appelé "serveur") qui va se charger de répartir les communications entre les clients.

  • Architecture Peer-To-Peer

Une architecture Peer-To-Peer (P2P) : ce mode plus complexe est dit décentralisé, car il n'y a pas de serveur. Chaque client peut communiquer directement avec un autre client. C'est plus direct, ça évite d'encombrer un serveur, mais c'est plus délicat à mettre en place.

Nous, nous allons utiliser une architecture client / serveur, la plus simple.

Il va en fait falloir faire non pas un mais deux projets :

  • Un projet "serveur" : pour créer le programme qui va répartir les messages entre les clients.
  • Un projet "client" : pour chaque client qui participera au Chat.

Vous n'êtes pas obligés d'utiliser une machine spécialement pour faire serveur. L'une des machines des clients peut aussi faire office de serveur. Il suffira de faire tourner un programme "serveur" en même temps qu'un programme "client", c'est tout à fait possible.

En pratique donc, une seule personne lancera le programme "serveur" et le programme "client" à la fois, et toutes les autres lanceront uniquement le programme "client".

Principe de fonctionnement du Chat

Le principe du Chat est simple : une personne écrit un message, et tout le monde reçoit ce message sur son écran.

Les choses se passent en 2 temps :

Vincent envoie un message au serveur

Un client envoie un message au serveur.

Le serveur renvoie le message à tout le monde (y compris Vincent)

Le serveur renvoie ce message à tous les clients pour qu'il s'affiche sur leur fenêtre.

Pourquoi le serveur renverrait-il le message à Vincent, vu que c'est lui qui l'a envoyé ?

On peut gérer les choses de plusieurs manières. On pourrait s'arranger pour que le serveur n'envoie pas le message à Vincent pour éviter un trafic réseau inutile, mais cela compliquerait un petit peu le programme.
Il est plus simple de faire en sorte que le serveur renvoie le message à tout le monde sans distinction. Vincent verra donc son message s'afficher sur son écran de discussion uniquement quand le serveur l'aura reçu et le lui aura renvoyé. Cela permet de vérifier en outre que la communication sur le réseau fonctionne correctement.

Structure des paquets

Les messages qui circuleront sur le réseau seront placés dans des paquets. C'est à nous de définir la structure des paquets que l'on veut envoyer.

Par exemple, quand Vincent va envoyer un message, un paquet va être créé avant d'être envoyé sur le réseau. Voici la structure de paquet que je propose pour notre programme de Chat :

Le paquet est constitué de 2 parties :

  • tailleMessage : un nombre entier qui sert à indiquer la taille du message qui suit. Cela permet au serveur de connaître la taille totale du message envoyé, pour qu'il puisse savoir quand il a reçu le message en entier.

Ce nombre ne sera pas de type int comme on aurait pu s'y attendre mais de type quint16. En effet, le type int peut avoir une taille différente selon les machines sur le réseau (un int peut prendre 16 bits de mémoire sur une machine, et 8 bits sur une autre).
Pour résoudre le problème, on utilise un type spécial de Qt, le quint16, qui correspond à un nombre entier prenant 16 bits de mémoire quelle que soit la machine (quand je vous avais dit que c'était bas niveau ).
quint16 signifie : "Qt Unsigned Int 16", soit "Entier non signé codé sur 16 bits".

  • message : c'est le message envoyé par le client. Ce message sera de type QString (ça c'est simple, vous connaissez !).

Pourquoi envoie-t-on la taille du message en premier ? On ne pourrait pas envoyer le message tout court ?

Il faut savoir que le protocole TCP va découper le paquet en sous-paquets avant de l'envoyer sur le réseau. Il n'enverra peut-être pas tout d'un coup. Par exemple, notre paquet pourrait être découpé comme ceci :

On n'a aucun contrôle sur la taille de ces sous-paquets, et il n'y a aucun moyen de savoir à l'avance comment ça va être découpé.
Le problème, c'est que le serveur va recevoir ces paquets petit à petit, et non pas tout d'un coup. Il ne peut pas savoir quand la totalité du message a été reçue.

Le protocole TCP ne permet pas de contrôler la taille des sous-paquets ni leur nombre, par contre il s'arrange pour que les paquets arrivent à destination dans le bon ordre (ce qui est pratique, parce que sinon ça aurait été encore plus compliqué à remettre en ordre ).
Pour information, le protocole UDP, qui est plus rapide, ne fait aucun contrôle sur l'ordre des paquets envoyés !

Bon, il faut qu'on arrive à savoir quand on a reçu le message en entier, et donc quand ce n'est plus la peine d'attendre de nouveaux sous-paquets.
Pour résoudre ce problème, on envoie la taille du message dans un premier temps. Lorsque la taille du message a été reçue, on va attendre que le message soit au complet. On se base sur tailleMessage pour savoir combien d'octets il nous reste à recevoir.

Lorsqu'on a récupéré tous les octets restants du paquet, on sait que le paquet est au complet, et cela veut dire qu'on a donc reçu le message entier.


Réalisation du serveur

Comme je vous l'ai dit, nous allons devoir réaliser 2 projets :

  • Un projet "client"
  • Un projet "serveur"

Créé avec HelpNDoc Personal Edition: Dites adieu aux soucis de documentation avec un outil de création d'aide