Développement d'un serveur TCP de traduction et gestion des adresses IP en C++

Pour transformer un serveur TCP basique en un service utile, il suffit d'adapter la logique de traitement des données reçues. Dans cet article, nous allons concevoir un serveur de traduction Anglais-Français simple en s'appuyant sur une architecture de sockets TCP. Nous aborderons également les fonctions essentielles de manipulation d'adresses IP et le cycle de vie d'une connexion TCP.

Conception du module de traduction (LexiconManager.hpp)

Le rôle de ce module est de charger un fichier dictionnaire, de parser son contenu dans une structure de données en mémoire, puis de répondre aux requêtes de traduction. Voici une implémentation optimisée utilisant une table de hachage.

#include <iostream>
#include <string>
#include <fstream>
#include <unordered_map>
#include <vector>

const std::string CHEMIN_DICO = "./data/lexique.txt";
const std::string VALEUR_INCONNUE = "Mot introuvable";
const std::string DELIMITEUR = ":";

class LexiconManager {
public:
    LexiconManager(std::string chemin = CHEMIN_DICO) : _cheminFichier(chemin) {
        if (chargerFichier()) {
            extrairePaires();
        }
    }

    // Recherche la traduction d'un mot
    std::string traduire(const std::string &mot) {
        auto it = _tableTraductions.find(mot);
        if (it == _tableTraductions.end()) {
            return VALEUR_INCONNUE;
        }
        return it->second;
    }

private:
    bool chargerFichier() {
        std::ifstream fichier(_cheminFichier);
        if (!fichier.is_open()) return false;

        std::string ligne;
        while (std::getline(fichier, ligne)) {
            _lignesBrutes.push_back(ligne);
        }
        fichier.close();
        return true;
    }

    void extrairePaires() {
        for (const auto &ligne : _lignesBrutes) {
            size_t index = ligne.find(DELIMITEUR);
            if (index == std::string::npos) continue;

            std::string cle = ligne.substr(0, index);
            std::string valeur = ligne.substr(index + DELIMITEUR.length());
            _tableTraductions[cle] = valeur;
        }
    }

    std::string _cheminFichier;
    std::unordered_map<std::string, std::string> _tableTraductions;
    std::vector<std::string> _lignesBrutes;
};

Intégration dans la routine du serveur TCP

Pour intégrer ce service, nous modifions la fonction de traitement (souvent appelée Service ou Handler) pour qu'elle utilise notre LexiconManager au lieu de simplement renvoyer l'écho du message.

#include "LexiconManager.hpp"
#include <sys/socket.h>
#include <unistd.h>

// Instance globale ou membre de classe pour la traduction
LexiconManager dico;

void TraiterRequeteClient(int client_fd) {
    char tampon[1024];
    while (true) {
        ssize_t octets_lus = recv(client_fd, tampon, sizeof(tampon) - 1, 0);
        if (octets_lus > 0) {
            tampon[octets_lus] = '\0';
            
            // Logique de traduction
            std::string resultat = dico.traduire(tampon);
            
            // Envoi de la réponse
            send(client_fd, resultat.c_str(), resultat.size(), 0);
        } 
        else if (octets_lus == 0) {
            // Le client a fermé la connexion
            break;
        } 
        else {
            perror("Erreur de lecture");
            break;
        }
    }
    close(client_fd);
}

Manipulation des adresses IP en réseau

Le passage d'une adresse IP sous forme de chaîne (ex: "192.168.1.1") à une structure binaire compréhensible par la pile réseau est une étape cruciale.

Conversion de Chaîne vers Entier (Binaire)

  • inet_aton : Convertit une chaîne IPv4 en une structure in_addr. Retourne 1 en cas de succès.
  • inet_addr : Méthode simplifiée retournant l'adresse sous forme de in_addr_t. Retourne INADDR_NONE en cas d'échec.
  • inet_pton : La version moderne (Presentation to Network). Elle est compatible IPv4 et IPv6. Retourne 1 si succès, 0 si l'adresse est invalide.

Conversion d'Entier vers Chaîne

  • inet_ntoa : Prend une structure in_addr et retourne un pointeur vers une chaîne de caractères statique. Attention : non sécurisé pour le multithreading.
  • inet_ntop : La version moderne (Network to Presentation). Elle nécessite un tampon de destination et sa taille, ce qui la rend sûre pour le multithreading.

Le protocloe de communication TCP

TCP (Transmission Control Protocol) assure une livraison fiable et ordonnée des données via un mécanisme de connexion structuré.

L'établissement de connexion (Three-Way Handshake)

  1. SYN : Le client envoie un segment de synchronisation pour initier la connexion.
  2. SYN-ACK : Le serveur répond pour confirmer la réception et synchroniser ses propres paramètres.
  3. ACK : Le client confirme la réception du signal du serveur. La connexion est alors "ESTABLISHED".

Le transfert de données

TCP est un protocole Full-Duplex, ce qui signifie que les deux parties peuvent transmettre et recevoir des données simultanément sur le même canal. Le flux est géré comme un flux d'octets continu (byte stream), similaire à la lecture d'un fichier ou d'un tube (pipe).

La fermeture de connexion (Four-Way Handshake)

  1. FIN (Client) : Le client demande la fermeture de sa moitié de la connexion.
  2. ACK (Serveur) : Le serveur accuse réception. L'application serveur reçoit un signal de fin de fichier (read retourne 0).
  3. FIN (Serveur) : Une fois que le serveur a fini d'envoyer ses dernières données, il envoie son propre segment de fermeture.
  4. ACK (Client) : Le client confirme, et la connexion est totalement libérée après un délai d'attente de sécurité.

Étiquettes: C++ TCP sockets Network-Programming IP-Management

Publié le 13 juin à 03h02