Les Flux TCP et Dialogue TCP/IP jettent les bases permettant la réalisation d'une librairie dédiée aux communications point-à-point TCP/IP. Cette librairie réalisée sous LINUX en C++ (C++18) reposent sur des concepts qu'il faut connaître avant de pouvoir en tirer pleinement parti.
Le premier principe est que le dialogue repose sur ensemble de couples (requête, réponse). Ce n'est pas le contenu qui fixe ici la différence entre requête est réponse mais l'initiateur du sous-dialogue : l'initiateur émet une requête pour laquelle le destinataire délivre une réponse. Par la suite et afin d'alléger l'écriture, on parle de message pour référencer indifféremment une requête ou une réponse. Autrement dit, un dialogue est un échange de messages.
Tout serait plus simple si TCP/IP et les mémoires vives des hôtes en correspondance permettaient d'émettre un message en une seule fois sur le canal de communication quelle que soit sa taille. Il est facile d'imaginer que si la réponse consiste à émettre un fichier de plusieurs giga-octets cela ne peut pas se faire en une seule fois.
En d'autre terme, il faut pouvoir passer de la vision du canal de communication à celle de messages échangés. C'est une peu comme si on essayait de faire transiter un gros volume d'eau a travers un tuyau avec un bac réceptacle de faible volume. Pour cela, nous devons construire une infrastructure qui partant du niveau le plus bas (le socket ou "connecteur" IP) nous amène à un niveau d'abstraction suffisant pour "penser" message. Ainsi, l'infrastrcuture comporte diverses classes qui découpent la communication en strates.
![]() | Pour passer de la vision "flux réseau" à celle "d'échange de messages", nous avons conçu un modèle en couches. L'emploi de chaque couche est facultatif bien que les méthodes statiques de la classe NetHelper soient une réelle facilité. |
Pour reprendre notre analogie hydraulique, pour permettre au gros volume d'eau de transiter par notre tuyau, il faut que l'extémité réceptice du tuyau ferme le robinet d'arrivée à chaque fois que le bac récepteur est plein, puis vide ce bac dans un gros contenant avant de pouvoir réouvrir le robinet d'arrivée.
C'est ainsi qu'au plus bas niveau, nous avons besoin d'une classe qui gère la fragmentation des émissions et des réceptions de messages. C'est le rôle de la classe NetHelper. Il est tout à fait possible dans le cadre d'applications simples comme un CHAT d'utiliser directement cette seule classe pour communiquer.
La classe NetHelper est cependant assez limitée dès lors que la dynamique du volume des échanges est très variable (de quelques octets à plusieurs giga) ou que l'on souhaite disposer de services de haut niveau comme le chiffrement transparent.
La classe NetDialog est chargée de répondre à ce besoin. Elle dispose des méthodes receive et sendchargées d'abstraire les va-et-vient des segments de données qui constituent l'ensemble d'un même message. Ces va-et-vient reposent sur le protocole simple décrit dans l'article Dialogue TCP/IP. Elle permet également un chiffrement transparent des messages sur le réseau.
La dernière couche est constituée des classes applicatives d'interprétaion des réponses reçus et de mise en forme pour leur transport des requêtes. Lors de la réception d'une requête, la classe NetDialog livre un tampon contenant les données reçues. Si le volume des données est très important elles sont stockées dans un fichier temporaire. Pour éviter à l'utilisateur de jongler entre données en mémoire et données sur media, le tampon est intégé à la classe NetBuffer. Un objet de cette même classe est utilisée pour stocker les données des requêtes à émettre. Notez que si les données sont stockées dans un fichier temporaire, elles le sont toujours sous forme déchiffrées. Ce fichier est automatiquement supprimé lors de la destruction de l'objet de classe NetBuffer qui l'a créé.
On peut donc voir un tampon de classe NetBuffer comme la sérialisation d'un flux qui représente un message (requête ou réponse). Ce message peut être lui-même un objet instancié depuis une classe. Pour faciliter ce travail, la librairie propose la classe abstraite NetMessage. Pour cette classe, la sérialisation débute par 4 octets formattés petit indien qui affectent au message un identifiant qui devrait être unique (choix à la construction de l'objet dérivé). Cet identifiant caractéristique de la classe du message permet de s'assurer que le flux reçu correspond à la sérialisation d'un objet de la classe attendue. La classe NetMessage est abstraite. Cela signifie qu'elle ne peut pas être directement instanciée mais doit être dérivée par un classe concrète qui a l'obligation d'implanter les méthodes serialize() et deserialize().
Rédaction par Jean-Marie Piatte (1983-2021)