Les bases du transfert de données fiable

Les bases du transfert de données fiable

À ceux qui vise s'intĂ©ressent aux rĂ©seaux et aux protocoles.

En résumé

Cet article aborde les bases de la transmission fiable des données, avec des exemples sur Go, y compris UDP et TCP. Inspiré par un, deux, trois et le livre "Réseaux informatiques. Approche descendante", car tout le monde parle seulement de Tanenbaum et d'Olifer.

Le protocole de transport

assure une connexion logique entre les processus applicatifs s'exécutant sur différents hÎtes. La connexion logique du point de vue des applications apparaßt comme un canal reliant directement les processus.

Les bases du transfert de données fiable

Les protocoles de transport sont pris en charge par les systĂšmes finaux, mais pas par les routeurs de rĂ©seau (sauf pour — DPI). Du cĂŽtĂ© de l'Ă©metteur, le niveau de transport convertit les donnĂ©es du niveau applicatif, reçues du processus applicatif Ă©metteur, en paquets de transport, appelĂ©s segments.

Les bases du transfert de données fiable

Cela se fait en fragmentant (si nĂ©cessaire) les messages du niveau applicatif en morceaux et en ajoutant un en-tĂȘte de niveau de transport Ă  chacun d'eux.

Les bases du transfert de données fiable

Ensuite, le niveau de transport transmet le segment au niveau rĂ©seau de l'Ă©metteur, oĂč le segment est encapsulĂ© dans un paquet de niveau rĂ©seau (datagramme) et envoyĂ©. Du cĂŽtĂ© rĂ©cepteur, le niveau rĂ©seau extrait le segment de niveau transport du datagramme et le transmet au niveau de transport. Ensuite, le niveau de transport traite le segment reçu de maniĂšre Ă  rendre ses donnĂ©es accessibles Ă  l'application rĂ©ceptrice.

Les bases du transfert de données fiable

Principes de transmission fiable des données

Transmission fiable des données sur un canal parfaitement fiable

C'est le cas le plus simple. La partie émettrice reçoit simplement les données du niveau supérieur, crée un paquet contenant ces données et l'envoie dans le canal.

Serveur

package main

import (
    "log"
    "net"
)

func main() {
    // Adresse IP du serveur et port
    serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
    if err != nil {
        log.Fatal(err)
    }

    // créer un socket avec le port
    serverConn, err := net.ListenUDP("udp", serverAddr)
    if err != nil {
        log.Fatal(err)
    }
    // fermeture différée de la connexion
    defer serverConn.Close()

    // créer un buffer pour les données
    buf := make([]byte, 1024)

    // attendre la connexion
    for {
        // lire la demande
        n, addr, err := serverConn.ReadFromUDP(buf)
        // transmettre les donnĂ©es au NIVEAU SUPÉRIEUR : dans notre cas stdout
        println(string(buf[0:n]), " form ", addr.IP.String())
        if err != nil {
            log.Fatal(err)
        }
        // pas de réponse, car c'est UDP + canal fiable
    }
}

Client

package main

import (
    "fmt"
    "log"
    "net"
    "time"
)

func main() {
    // Adresse IP du serveur et port
    serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
    if err != nil {
        log.Fatal(err)
    }
    // Adresse IP locale et port
    localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
    if err != nil {
        log.Fatal(err)
    }
    // Établissement de la connexion
    conn, err := net.DialUDP("udp", localAddr, serverAddr)
    if err != nil {
        log.Fatal(err)
    }
    // Fermeture décalée de la connexion
    defer conn.Close()

    for {
        // RĂ©ception de donnĂ©es de la COUCHE SUPÉRIEURE
        fmt.Print("Entrez une phrase en clair > ")
        var msg string
        _, err := fmt.Scanf("%s", &msg)
        if err != nil {
            log.Fatal(err)
        }
        // Un flux de bytes est transmis, pas une chaĂźne
        buf := []byte(msg)
        // Écriture (transmission) dans la connexion
        _, err = conn.Write(buf)
        if err != nil {
            log.Fatal(err)
        }
        // Une seconde
        time.Sleep(time.Second * 1)
    }
}

Transmission fiable des données via un canal avec d'éventuelles erreurs

L'Ă©tape suivante consiste Ă  supposer que tous les paquets transmis sont reçus dans l'ordre dans lequel ils ont Ă©tĂ© envoyĂ©s, mais que des bits peuvent ĂȘtre endommagĂ©s, car le canal transmet parfois des donnĂ©es de maniĂšre altĂ©rĂ©e.

Les bases du transfert de données fiable

Dans ce cas, des mécanismes sont appliqués :

  • dĂ©tection des erreurs ;
  • retour d'information ;
  • retransmission.

Les protocoles de transmission fiable des données qui possÚdent de tels mécanismes de retransmission sont appelés protocoles à demande de retransmission automatique (Automatic Repeat reQuest, ARQ).
De plus, il convient de prévoir la possibilité d'erreurs dans les accusés de réception, lorsque la partie réceptrice ne reçoit aucune information sur les résultats de la transmission du dernier paquet.
La solution à ce problÚme, utilisée notamment dans TCP, consiste à ajouter un nouveau champ contenant le numéro de séquence du paquet dans le paquet de données.

Les bases du transfert de données fiable

Transmission fiable des données sur un canal non fiable, permettant la distorsion et la perte de paquets

Malheureusement, en plus de ces distorsions, la perte de paquets est également présente sur le réseau.
Et pour résoudre ce problÚme, des mécanismes sont nécessaires :

  • dĂ©tection de la perte de paquets ;
  • retransmission des paquets perdus Ă  la partie rĂ©ceptrice.

En plus de la perte de paquets, il est nĂ©cessaire de prĂ©voir la possibilitĂ© de la perte d'accusĂ©s de rĂ©ception ou, si rien n'est perdu, de leur livraison avec un retard significatif. Dans tous les cas, la mĂȘme action est effectuĂ©e : la retransmission du paquet. Un minuteur est utilisĂ© pour contrĂŽler le temps dans ce mĂ©canisme, permettant de dĂ©terminer la fin de l'intervalle d'attente. Ainsi, dans le paquet net le paramĂštre TCPKeepAlive est dĂ©fini sur 15 secondes par dĂ©faut :

// defaultTCPKeepAlive is a default constant value for TCPKeepAlive times
// See golang.org/issue/31510
const (
    defaultTCPKeepAlive = 15 * time.Second
)

Le cĂŽtĂ© Ă©metteur doit dĂ©marrer le minuteur chaque fois qu'il transmet un paquet (que ce soit lors de la premiĂšre transmission ou de la retransmission), gĂ©rer les interruptions du minuteur et l'arrĂȘter.

Ainsi, nous avons examiné les concepts clés des protocoles de transmission de données fiables :

  • les sommes de contrĂŽle ;
  • les numĂ©ros de sĂ©quence des paquets ;
  • les minuteurs ;
  • les accusĂ©s de rĂ©ception positifs et nĂ©gatifs.

Mais ce n'est pas tout !

Le protocole de transmission de données fiables avec mise en pipeline

Dans la version que nous avons déjà examinée, le protocole de livraison fiable est trÚs inefficace. Il commence à « freiner » la transmission assurée par le canal de communication lorsque le RTT augmente. Pour améliorer son efficacité et mieux utiliser la bande passante du canal de communication, la mise en pipeline est appliquée.

Les bases du transfert de données fiable

L'application de la mise en pipeline conduit Ă  :

  • une augmentation de la plage des numĂ©ros de sĂ©quence, car tous les paquets envoyĂ©s (Ă  l'exception des retransmissions) doivent ĂȘtre identifiables de maniĂšre unique ;
  • un besoin accru de buffers du cĂŽtĂ© de l'Ă©metteur et du rĂ©cepteur.

La plage des numéros de séquence et les exigences concernant la taille des buffers dépendent des actions entreprises par le protocole en réponse à la distorsion, à la perte et au retard des paquets. Dans le cas de la mise en pipeline, il existe deux méthodes de correction des erreurs :

  • retourner Ă  N paquets en arriĂšre ;
  • rĂ©pĂ©tition sĂ©lective.

Retourner Ă  N paquets en arriĂšre — le protocole Ă  fenĂȘtre glissante

Les bases du transfert de données fiable

L'expéditeur doit prendre en charge trois types d'événements :

  • appel via un protocole de niveau supĂ©rieur. Lorsque la fonction d'envoi de donnĂ©es est appelĂ©e « d'en haut », la partie Ă©mettrice vĂ©rifie d'abord le degrĂ© de remplissage de la fenĂȘtre (c'est-Ă -dire la prĂ©sence de N messages envoyĂ©s en attente d'accusĂ©s de rĂ©ception). Si la fenĂȘtre n'est pas remplie, un nouveau paquet est formĂ© et transmis, et les valeurs des variables sont mises Ă  jour. Dans le cas contraire, la partie Ă©mettrice renvoie les donnĂ©es au niveau supĂ©rieur, ce qui implique de maniĂšre implicite que la fenĂȘtre est remplie. En gĂ©nĂ©ral, le niveau supĂ©rieur tentera de retransmettre les donnĂ©es aprĂšs un certain temps. Dans une application rĂ©elle, l'expĂ©diteur aurait probablement soit mis en mĂ©moire tampon les donnĂ©es (au lieu de les envoyer immĂ©diatement), soit disposĂ© d'un mĂ©canisme de synchronisation (comme un sĂ©maphore ou un drapeau) permettant au niveau supĂ©rieur d'appeler la fonction d'envoi de donnĂ©es uniquement lorsque la fenĂȘtre n'est pas remplie.
  • accusĂ© de rĂ©ception. Dans le protocole pour un paquet avec le numĂ©ro de sĂ©quence N, un accusĂ© gĂ©nĂ©ral est Ă©mis, indiquant que tous les paquets avec des numĂ©ros de sĂ©quence prĂ©cĂ©dant N ont Ă©tĂ© acceptĂ©s avec succĂšs.
  • expiration du dĂ©lai d'attente. Pour dĂ©terminer les pertes et les retards de paquets et d'accusĂ©s de rĂ©ception, le protocole utilise un minuteur. Si le dĂ©lai d'attente expire, la partie Ă©mettrice renvoie tous les paquets non confirmĂ©s.

répétition sélective

Lorsque la taille de la fenĂȘtre et le produit de la bande passante par le dĂ©lai de propagation sont Ă©levĂ©s, un grand nombre de paquets peuvent ĂȘtre en transit. Dans ce cas, une erreur d'un paquet particulier peut entraĂźner la retransmission d'un grand nombre de paquets, dont la plupart n'Ă©taient pas nĂ©cessaires.

Exemple

Meilleures thĂ©oriques pratiques rassemblĂ©es dans une mise en Ɠuvre pratique TCP. Et si quelqu'un sait comment faire mieux — bienvenue.

Serveur

package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
    "strings"
)

func main() {
    // créer un socket sur le port 
    ln, err := net.Listen("tcp", ":8081")
    if err != nil {
        log.Fatalln(err)
    }
    // attente d'un appel
    conn, _ := ln.Accept()

    for {
        // lecture des données
        msg, err := bufio.NewReader(conn).ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // affichage du message dans stdout
        fmt.Print("Message reçu:", string(msg))
        // conversion de la chaĂźne en majuscules
        newMsg := strings.ToUpper(msg)
        // envoi des données
        conn.Write([]byte(newMsg + "n"))
    }
}

Client

package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
    "os"
)

func main() {
    // établissant la connexion
    conn, err := net.Dial("tcp", "127.0.0.1:8081")
    if err != nil {
        log.Fatalln(err)
    }

    for {
        // lecture des données depuis stdin
        reader := bufio.NewReader(os.Stdin)
        fmt.Print("Texte Ă  envoyer : ")
        // ligne par ligne
        text, err := reader.ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // envoi
        fmt.Fprintf(conn, text+"n")
        // réception
        msg, err := bufio.NewReader(conn).ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // affichage de la réponse reçue
        fmt.Print("Msg du serveur : " + msg)
    }
}

Sortie

Mécanismes garantissant une transmission fiable des données et leur utilisation

Le mécanisme
Application, commentaire

Somme de contrĂŽle
Utilisée pour détecter les erreurs de bits dans le paquet transmis

ChronomĂštre
DĂ©lai d'attente et indication de son expiration. Cela signifie qu'il est trĂšs probable que le paquet ou son accusĂ© de rĂ©ception ont Ă©tĂ© perdus lors de la transmission. Dans le cas oĂč le paquet est livrĂ© avec retard mais n'est pas perdu (expiration prĂ©maturĂ©e du dĂ©lai d'attente), ou s'il y a une perte de l'accusĂ© de rĂ©ception, une retransmission entraĂźne un doublon du paquet du cĂŽtĂ© du rĂ©cepteur

Numéro de séquence
Utilisé pour la numérotation séquentielle des paquets de données transmis de l'expéditeur au destinataire. Les écarts dans les numéros de séquence des paquets reçus permettent au destinataire de détecter la perte de paquet. Des numéros de séquence identiques signifient que les paquets se dupliquent

Accusé de réception
Généré par le récepteur et indique à l'expéditeur que le paquet ou le groupe de paquets correspondant a été reçu avec succÚs. En général, l'accusé de réception contient les numéros de séquence des paquets reçus avec succÚs. Selon le protocole, on distingue les accusés de réception individuels et groupés

Accusé de réception négatif
Utilisé par le récepteur pour informer l'expéditeur que le paquet a été reçu de maniÚre incorrecte. L'accusé de réception négatif inclut généralement le numéro de séquence du paquet qui n'a pas été correctement reçu

FenĂȘtre, pipeline
Ils limitent la plage des numĂ©ros de sĂ©quence qui peuvent ĂȘtre utilisĂ©s pour la transmission de paquets. La diffusion groupĂ©e et la poignĂ©e de main permettent d'augmenter considĂ©rablement la capacitĂ© des protocoles par rapport au mode d'attente des accusĂ©s de rĂ©ception. Comme nous le verrons, la taille de la fenĂȘtre peut ĂȘtre calculĂ©e en fonction des capacitĂ©s de rĂ©ception et de mise en tampon du cĂŽtĂ© rĂ©cepteur, ainsi que du niveau de congestion du rĂ©seau.

D'autres exemples d'utilisation de Go pour le travail en réseau.

Dans dépÎts.

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster