Basisprincipes van betrouwbare gegevensoverdracht

Basisprincipes van betrouwbare gegevensoverdracht

Voor degenen die zoekt Toegewijd aan het begrijpen van netwerken en protocollen.

Kort

Het artikel bespreekt de basisprincipes van betrouwbare gegevensoverdracht en implementeert voorbeelden Go, inclusief UDP en TCP. Gebaseerd op tijd, два, drie en de boeken "Computernetwerken. Top-Down Approach", anders heeft iedereen het alleen over Tannenbaum en Oliferov.

Transportlaagprotocol

Biedt een logische verbinding tussen applicatieprocessen die op verschillende hosts draaien. Vanuit applicatieperspectief lijkt een logische verbinding op een kanaal dat processen rechtstreeks met elkaar verbindt.

Basisprincipes van betrouwbare gegevensoverdracht

Transportlaagprotocollen worden ondersteund door eindsystemen, maar niet door netwerkrouters (behalve - DPI). Aan de zenderkant converteert de transportlaag de applicatielaaggegevens die hij ontvangt van het verzendende applicatieproces naar transportlaagpakketten die segmenten worden genoemd.

Basisprincipes van betrouwbare gegevensoverdracht

Dit wordt gedaan door (indien nodig) de berichten op de applicatielaag in fragmenten te splitsen en aan elk ervan een header van de transportlaag toe te voegen.

Basisprincipes van betrouwbare gegevensoverdracht

De transportlaag geeft het segment vervolgens door aan de netwerklaag van de afzender, waar het segment wordt ingekapseld in een netwerklaagpakket (datagram) en verzonden. Aan de ontvangende kant extraheert de netwerklaag het transportlaagsegment uit het datagram en geeft dit door aan de transportlaag. Vervolgens verwerkt de transportlaag het ontvangen segment zodat de gegevens ervan beschikbaar komen voor de ontvangende applicatie.

Basisprincipes van betrouwbare gegevensoverdracht

Principes van betrouwbare datatransmissie

Betrouwbare datatransmissie via een volledig beveiligd kanaal

Het eenvoudigste geval. De verzendende kant ontvangt eenvoudigweg de gegevens van de bovenste laag, maakt een pakket aan dat deze gegevens bevat en verzendt deze naar het kanaal.

Server

package main

import (
    "log"
    "net"
)

func main() {
    // IP-адрес сервера и порт
    serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
    if err != nil {
        log.Fatal(err)
    }

    // создаем сокет с портом
    serverConn, err := net.ListenUDP("udp", serverAddr)
    if err != nil {
        log.Fatal(err)
    }
    // отложенное закрытие соединения
    defer serverConn.Close()

    // создаем буфер для данных
    buf := make([]byte, 1024)

    // ждем соединение
    for {
        // читаем запрос
        n, addr, err := serverConn.ReadFromUDP(buf)
        // передаем данные в ВЕРХНИЙ уровень: в нашем случае stdout
        println(string(buf[0:n]), " form ", addr.IP.String())
        if err != nil {
            log.Fatal(err)
        }
        // ответа нет, т.к. это UDP + надежный канал
    }
}

klant

package main

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

func main() {
    // IP-адрес сервера и порт
    serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
    if err != nil {
        log.Fatal(err)
    }
    // локальный IP-адрес и порт
    localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
    if err != nil {
        log.Fatal(err)
    }
    // установка соединения
    conn, err := net.DialUDP("udp", localAddr, serverAddr)
    if err != nil {
        log.Fatal(err)
    }
    // отложенное закрытие соединения
    defer conn.Close()

    for {
        // получение данных от ВЕРХНЕГО уровня
        fmt.Print("Введите строчное предложение > ")
        var msg string
        _, err := fmt.Scanf("%s", &msg)
        if err != nil {
            log.Fatal(err)
        }
        // передается поток байт, а не строка
        buf := []byte(msg)
        // запись (передача) в соединение
        _, err = conn.Write(buf)
        if err != nil {
            log.Fatal(err)
        }
        // 1 секундочку
        time.Sleep(time.Second * 1)
    }
}

Betrouwbare datatransmissie over een kanaal met mogelijke fouten

De volgende stap is om aan te nemen dat alle verzonden pakketten worden ontvangen in de volgorde waarin ze zijn verzonden, maar dat de bits daarin beschadigd kunnen raken vanwege het feit dat het kanaal soms gegevens met vervormingen verzendt.

Basisprincipes van betrouwbare gegevensoverdracht

In dit geval worden de volgende mechanismen gebruikt:

  • fout detectie;
  • feedback;
  • heruitzending.

Betrouwbare protocollen voor gegevensoverdracht die vergelijkbare mechanismen hebben voor het meerdere keren herhalen van de transmissie, worden ARQ-protocollen (Automatic Repeat ReQuest) genoemd.
Bovendien is het de moeite waard om de mogelijkheid van fouten in de ontvangstbewijzen te overwegen, wanneer de ontvangende partij geen informatie ontvangt over de resultaten van de overdracht van het laatste pakket.
De oplossing voor dit probleem, dat ook in TCP wordt gebruikt, is het toevoegen van een nieuw veld aan het datapakket met daarin het volgnummer van het pakket.

Basisprincipes van betrouwbare gegevensoverdracht

Betrouwbare gegevensoverdracht via een onbetrouwbaar kanaal dat onderhevig is aan pakketvervorming en -verlies

Naast vervorming is er helaas pakketverlies in het netwerk.
En om dit probleem op te lossen zijn mechanismen nodig:

  • het vaststellen van het feit van pakketverlies;
  • het opnieuw bezorgen van verloren pakketten aan de ontvangende partij.

Bovendien is het, naast het verlies van het pakket, noodzakelijk om te voorzien in de mogelijkheid van verlies van het ontvangstbewijs of, als er niets verloren gaat, van de levering ervan met een aanzienlijke vertraging. In alle gevallen wordt hetzelfde gedaan: het pakket wordt opnieuw verzonden. Om de tijd te controleren, maakt dit mechanisme gebruik van een afteltimer, waarmee u het einde van het wachtinterval kunt bepalen. Dus in het pakket netto TCPKeepAlive is standaard ingesteld op 15 seconden:

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

De verzendende kant moet elke keer dat een pakket wordt verzonden (zowel de eerste als de tweede keer) een timer starten, onderbrekingen van de timer afhandelen en deze stoppen.

We zijn dus vertrouwd geraakt met de sleutelconcepten van betrouwbare protocollen voor gegevensoverdracht:

  • controlesommen;
  • volgnummers van pakketten;
  • timers;
  • positieve en negatieve ontvangsten.

Maar dat is niet alles!

Betrouwbaar protocol voor gegevensoverdracht met pipelining

In de variant die we al hebben overwogen is het betrouwbare afleverprotocol zeer inefficiënt. Het begint de transmissie van het communicatiekanaal te “vertragen” naarmate de RTT toeneemt. Om de efficiëntie te vergroten en de capaciteit van het communicatiekanaal beter te benutten, wordt pipelining gebruikt.

Basisprincipes van betrouwbare gegevensoverdracht

Het gebruik van pipelining leidt tot:

  • het vergroten van het bereik van volgnummers, aangezien alle verzonden pakketten (behalve hertransmissies) uniek moeten worden geïdentificeerd;
  • de noodzaak om de buffers aan de zendende en ontvangende zijde te vergroten.

Het volgnummerbereik en de vereisten voor de buffergrootte zijn afhankelijk van de acties die het protocol onderneemt als reactie op pakketbeschadiging, verlies en vertraging. In het geval van pipelining zijn er twee methoden om fouten te corrigeren:

  • retourneer N pakketten terug;
  • selectieve herhaling.

N pakketten teruggaan - protocol met schuifvenster

Basisprincipes van betrouwbare gegevensoverdracht

De afzender moet drie soorten gebeurtenissen ondersteunen:

  • oproep via een protocol op een hoger niveau. Wanneer de gegevensverzendfunctie “van bovenaf” wordt aangeroepen, controleert de verzendende kant eerst de mate van vulling van het venster (dat wil zeggen, de aanwezigheid van N verzonden berichten in afwachting van ontvangst van ontvangstbewijzen). Als het venster leeg is, wordt een nieuw pakket gegenereerd en verzonden, en worden de variabele waarden bijgewerkt. Anders retourneert de verzendende kant gegevens naar de bovenste laag, en dit is een impliciete indicatie dat het venster vol is. Meestal zal de bovenste laag na enige tijd opnieuw proberen de gegevens te verzenden. In een echte toepassing zou de afzender waarschijnlijk de gegevens bufferen (in plaats van ze onmiddellijk te verzenden) of een synchronisatiemechanisme hebben (zoals een semafoor of vlag) waarmee de bovenste laag de verzendfunctie alleen kan aanroepen als het venster leeg is. .
  • bevestiging ontvangen. In het protocol wordt voor een pakket met volgnummer N een algemene bevestiging afgegeven die aangeeft dat alle pakketten met volgnummers die aan N voorafgaan, met succes zijn ontvangen.
  • het wachtinterval is verstreken. Om de feiten van verliezen en vertragingen van pakketten en ontvangstbewijzen te bepalen, maakt het protocol gebruik van een timer. Als het time-outinterval verstrijkt, verzendt de verzendende partij alle verzonden onbevestigde pakketten opnieuw.

Selectieve herhaling

Wanneer de venstergrootte en het vertragingsproduct van de doorvoerpropagatie groot zijn, kan er een groot aantal pakketten in de pijplijn zitten. In zo'n geval kan een enkele pakketfout ertoe leiden dat een groot aantal pakketten opnieuw wordt verzonden, waarvan de meeste niet nodig waren.

Voorbeeld

Top theoretisch Praktijken worden verzameld in de praktijkimplementatie TCP. En als iemand weet hoe het beste... welkom.

Server

package main

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

func main() {
    // создаем сокет с портом 
    ln, err := net.Listen("tcp", ":8081")
    if err != nil {
        log.Fatalln(err)
    }
    // ожидание вызова
    conn, _ := ln.Accept()

    for {
        // считывание данных
        msg, err := bufio.NewReader(conn).ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // вывод сообщения в stdout
        fmt.Print("Message Received:", string(msg))
        // перевод строки в верхний регистр
        newMsg := strings.ToUpper(msg)
        // отправка данных
        conn.Write([]byte(newMsg + "n"))
    }
}

klant

package main

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

func main() {
    // установка соединения
    conn, err := net.Dial("tcp", "127.0.0.1:8081")
    if err != nil {
        log.Fatalln(err)
    }

    for {
        // считывание данных с stdin
        reader := bufio.NewReader(os.Stdin)
        fmt.Print("Text to send: ")
        // построчно
        text, err := reader.ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // отправка
        fmt.Fprintf(conn, text+"n")
        // прием
        msg, err := bufio.NewReader(conn).ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // вывод полученного ответа
        fmt.Print("Msg from Server: " + msg)
    }
}

Uitgang

Mechanismen die een betrouwbare gegevensoverdracht en -gebruik garanderen

mechanisme
Toepassing, commentaar

Controleer som
Wordt gebruikt om bitfouten in een verzonden pakket te detecteren

Timer
Telt het time-outinterval af en geeft aan wanneer dit is verstreken. Dit laatste betekent dat het pakket of de ontvangst ervan met een hoge mate van waarschijnlijkheid verloren gaat tijdens de verzending. Als een pakket met vertraging wordt afgeleverd, maar niet verloren gaat (voortijdig verstrijken van het time-outinterval), of als een ontvangstbewijs verloren gaat, leidt hertransmissie tot een duplicaatpakket aan de ontvangende kant

Serienummer
Wordt gebruikt voor het opeenvolgend nummeren van datapakketten die van zender naar ontvanger worden verzonden. Door gaten in de volgnummers van ontvangen pakketten kan de ontvanger pakketverlies detecteren. Dezelfde pakketvolgnummers betekenen dat de pakketten duplicaten van elkaar zijn

Bevestiging
Gegenereerd door de ontvangende kant en geeft aan de verzendende kant aan dat het corresponderende pakket of de groep pakketten met succes is ontvangen. Normaal gesproken bevat de bevestiging de volgnummers van succesvol ontvangen pakketten. Afhankelijk van het protocol wordt onderscheid gemaakt tussen individuele en groepsbevestigingen

Negatieve bevestiging
Wordt door de ontvanger gebruikt om de afzender te informeren dat het pakket onjuist is ontvangen. Een negatieve bevestiging bevat doorgaans het volgnummer van het pakket dat niet correct is ontvangen

Venster, transportband
Beperk het bereik van volgnummers die kunnen worden gebruikt om pakketten te verzenden. Multicast en handshake kunnen de protocoldoorvoer aanzienlijk verhogen vergeleken met wachten op bevestigingen. Zoals we zullen zien, kan de venstergrootte worden berekend op basis van de ontvangst- en buffercapaciteiten van de ontvangende kant, evenals het netwerkbelastingsniveau

Meer voorbeelden van het gebruik van Go voor netwerken

В opslagplaatsen.

Bron: www.habr.com

Voeg een reactie