Grunnleggende om pålitelig dataoverføring

Grunnleggende om pålitelig dataoverføring

Til de som søker Dedikert til å forstå nettverk og protokoller.

kort

Artikkelen diskuterer det grunnleggende for pålitelig dataoverføring og implementerer eksempler på Go, inkludert UDP og TCP. Basert på tid, два, tre og boken «Computer Networks: A Top-Down Approach», ellers diskuterer alle bare Tannenbaum og Oliferov.

Transportlagsprotokoll

Gir en logisk forbindelse mellom applikasjonsprosesser som kjører på forskjellige verter. Fra applikasjonens perspektiv fremstår en logisk forbindelse som en kanal som direkte forbinder prosesser.

Grunnleggende om pålitelig dataoverføring

Transportlagsprotokoller støttet av sluttsystemer, men ikke av nettverksrutere (unntatt - DPIPå avsendersiden konverterer transportlaget applikasjonslagsdataene det mottar fra den sendende applikasjonsprosessen til transportlagspakker kalt segmenter.

Grunnleggende om pålitelig dataoverføring

Dette gjøres ved å dele opp (om nødvendig) meldinger på applikasjonslaget i fragmenter og legge til en transportlagsheader til hvert fragment.

Grunnleggende om pålitelig dataoverføring

Transportlaget sender deretter segmentet til avsenderens nettverkslag, hvor det innkapsles i en nettverkslagpakke (datagram) og sendes. På mottakersiden trekker nettverkslaget ut transportlagsegmentet fra datagrammet og sender det videre til transportlaget. Transportlaget behandler deretter det mottatte segmentet slik at dataene er tilgjengelige for mottakerapplikasjonen.

Grunnleggende om pålitelig dataoverføring

Prinsipper for sikker dataoverføring

Pålitelig dataoverføring over en fullstendig sikker kanal

Det enkleste tilfellet: Sendersiden mottar ganske enkelt data fra det øvre laget, lager en pakke som inneholder dem, og sender dem nedover kanalen.

Сервер

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 + надежный канал
    }
}

kunde

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)
    }
}

Pålitelig dataoverføring over en kanal med mulige feil

Neste trinn er å anta at alle overførte pakker mottas i den rekkefølgen de ble sendt, men bitene i dem kan være ødelagt på grunn av at kanalen noen ganger overfører data med forvrengninger.

Grunnleggende om pålitelig dataoverføring

I dette tilfellet brukes følgende mekanismer:

  • feildeteksjon;
  • tilbakemeldinger;
  • videresending.

Pålitelige dataoverføringsprotokoller som har slike mekanismer for flere overføringsrepetisjoner kalles ARQ-protokoller (Automatic Repeat ReQuest).
I tillegg er det verdt å vurdere muligheten for feil i mottak, når mottakeren ikke mottar noen informasjon om resultatene av den siste pakkeoverføringen.
Løsningen på dette problemet, som blant annet brukes i TCP, er å legge til et nytt felt i datapakken som inneholder pakkesekvensnummeret.

Grunnleggende om pålitelig dataoverføring

Pålitelig dataoverføring over en upålitelig kanal som tillater pakkeforvrengning og -tap

Sammen med forvrengninger er det dessverre pakketap i nettverket.
Og for å løse dette problemet kreves det mekanismer:

  • å avgjøre faktumet om pakketap;
  • omlevering av tapte pakker til mottakeren.

I tillegg til pakketap er det nødvendig å vurdere muligheten for tap av kvittering eller, hvis ingenting går tapt, levering med betydelig forsinkelse. I alle tilfeller skjer den samme handlingen: pakken sendes på nytt. For å kontrollere timingen bruker denne mekanismen en nedtellingstimer, som lar en bestemme slutten på ventetiden. Dermed, i pakken nett TCPKeepAlive er satt til 15 sekunder som standard:

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

Sendersiden må starte timeren hver gang en pakke sendes (både første og andre gang), håndtere avbrudd fra timeren og stoppe den.

Så har vi blitt kjent med nøkkelbegrepene for pålitelige dataoverføringsprotokoller:

  • sjekksummer;
  • serienumre på pakker;
  • tidtakere;
  • positive og negative mottakelser.

Men det er ikke alt!

Pålitelig dataoverføringsprotokoll med pipelining

I implementeringen vi allerede har vurdert, er den pålitelige leveringsprotokollen svært ineffektiv. Den begynner å "bremse ned" overføringen som leveres av kommunikasjonskanalen etter hvert som RTT øker. For å forbedre effektiviteten og bedre utnytte kommunikasjonskanalens båndbredde, brukes pipelining.

Grunnleggende om pålitelig dataoverføring

Bruk av rørledning resulterer i:

  • øke rekkevidden av sekvensnumre, siden alle sendte pakker (unntatt retransmisjoner) må være unikt identifiserbare;
  • behovet for å øke bufferne på sende- og mottakersiden.

Utvalget av sekvensnumre og krav til bufferstørrelse avhenger av protokollens respons på pakkekorrupsjon, tap og forsinkelse. Når det gjelder pipelining, finnes det to feilrettingsmetoder:

  • returner N pakker tilbake;
  • selektiv repetisjon.

Bakovergående N-pakker - glidende vinduprotokoll

Grunnleggende om pålitelig dataoverføring

Avsenderen må støtte tre typer hendelser:

  • Et kall fra en protokoll på høyere nivå. Når sendefunksjonen kalles ovenfra, sjekker avsenderen først om vinduet er fullt (det vil si om det er N sendte meldinger som venter på å bli bekreftet). Hvis vinduet ikke er fullt, dannes og sendes en ny pakke, og verdiene til variablene oppdateres. Ellers returnerer avsenderen dataene til det øvre laget, noe som er en implisitt indikasjon på at vinduet er fullt. Vanligvis vil det øvre laget prøve å overføre dataene på nytt etter en stund. I en reell applikasjon ville avsenderen mest sannsynlig enten bufre dataene (i stedet for å sende dem umiddelbart) eller ha en synkroniseringsmekanisme (f.eks. en semafor eller et flagg) som tillater det øvre laget å kalle sendefunksjonen bare når vinduet ikke er fullt.
  • Motta en bekreftelse. I protokollen utstedes en generell bekreftelse for en pakke med sekvensnummer N, som indikerer at alle pakker med sekvensnumre foran N er mottatt.
  • Utløp av tidsavbrudd. Protokollen bruker en timer for å oppdage tap og forsinkelser av pakker og bekreftelser. Hvis tidsavbruddet utløper, sender den sendende parten alle ubekreftede pakker på nytt.

Selektiv repetisjon

Når vindusstørrelsen og gjennomstrømningsforsinkelsesproduktet er stort, kan et stort antall pakker være underveis. I dette tilfellet kan en feil i en enkelt pakke føre til at et stort antall pakker sendes på nytt, hvorav de fleste ikke var nødvendige.

Eksempel

Topp teoretisk praksiser samles inn i praktisk implementering TCPOg hvis noen vet en bedre måte - velkommen.

Сервер

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"))
    }
}

kunde

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)
    }
}

Utgang

Mekanismer som sikrer pålitelig dataoverføring og -bruk

Механизм
Søknad, kommentarer

Sjekk sum
Brukes til å oppdage bitfeil i en overført pakke.

tidsur
Nedtelling av tidsavbruddsintervall og indikasjon på utløpsdato. Sistnevnte indikerer at pakken eller bekreftelsen med høy sannsynlighet gikk tapt under overføring. Hvis pakken leveres sent, men ikke går tapt (for tidlig utløp av tidsavbruddsintervallet), eller bekreftelsen går tapt, resulterer ny overføring i en duplikatpakke hos mottakeren.

Serienummer
Brukes til å sekvensnummerere datapakker som sendes fra sender til mottaker. Hull i sekvensnumrene til mottatte pakker lar mottakeren oppdage pakketap. Identiske pakkesekvensnumre indikerer dupliserte pakker.

Bekreftelse
Genereres av mottakeren og indikerer til senderen at den tilsvarende pakken eller gruppen av pakker er mottatt. Bekreftelser inneholder vanligvis sekvensnumrene til mottatte pakker. Avhengig av protokollen skilles det mellom individuelle og gruppebekreftelser.

Negativ bekreftelse
Brukes av mottakeren til å informere avsenderen om at en pakke ikke ble mottatt riktig. En negativ bekreftelse inkluderer vanligvis sekvensnummeret til pakken som ikke ble mottatt riktig.

Vindu, rørledning
De begrenser utvalget av sekvensnumre som kan brukes til pakkeoverføring. Multicast-overføring og håndtrykk øker protokollgjennomstrømningen betydelig sammenlignet med å vente på bekreftelser. Som vi skal se, kan vindusstørrelsen beregnes basert på mottakerens mottaks- og bufferkapasitet, samt nettverksbelastningen.

Andre eksempler på bruk av Go for nettverksbygging

В depoter.

Kilde: www.habr.com

Legg til en kommentar