Grundlæggende om pålidelig dataoverførsel

Grundlæggende om pålidelig dataoverførsel

Til dem, der søger Dedikeret til at forstå netværk og protokoller.

Kort

Artiklen diskuterer det grundlæggende i pålidelig datatransmission, implementerer eksempler på Go, herunder UDP og TCP. Baseret på tid, два, tre og bøgerne "Computer Networks. Top-Down Approach", ellers diskuterer alle kun Tannenbaum og Oliferov.

Transportlagsprotokol

Giver en logisk forbindelse mellem applikationsprocesser, der kører på forskellige værter. Fra et applikationsperspektiv ligner en logisk forbindelse en kanal, der direkte forbinder processer.

Grundlæggende om pålidelig dataoverførsel

Transportlagsprotokoller understøttes af slutsystemer, men ikke af netværksroutere (undtagen - DPI). På afsendersiden konverterer transportlaget de applikationslagsdata, det modtager fra afsenderapplikationsprocessen, til transportlagspakker kaldet segmenter.

Grundlæggende om pålidelig dataoverførsel

Dette gøres ved at opdele (om nødvendigt) applikationslagsmeddelelserne i fragmenter og tilføje en transportlagsheader til hver af dem.

Grundlæggende om pålidelig dataoverførsel

Transportlaget sender derefter segmentet videre til afsenderens netværkslag, hvor segmentet indkapsles i en netværkslagspakke (datagram) og sendes. I den modtagende ende udtrækker netværkslaget transportlagsegmentet fra datagrammet og sender det op til transportlaget. Dernæst behandler transportlaget det modtagne segment, så dets data bliver tilgængelige for den modtagende applikation.

Grundlæggende om pålidelig dataoverførsel

Principper for pålidelig datatransmission

Pålidelig dataoverførsel over en fuldstændig sikker kanal

Den enkleste sag. Afsendersiden modtager simpelthen dataene fra det øverste lag, opretter en pakke, der indeholder dem, og sender dem til kanalen.

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

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ålidelig datatransmission over en kanal med mulige fejl

Det næste trin er at antage, at alle transmitterede pakker modtages i den rækkefølge, de blev sendt i, men bits i dem kan være beskadiget på grund af det faktum, at kanalen nogle gange transmitterer data med forvrængninger.

Grundlæggende om pålidelig dataoverførsel

I dette tilfælde anvendes følgende mekanismer:

  • fejlfinding;
  • feedback;
  • genudsendelse.

Pålidelige dataoverførselsprotokoller, der har lignende mekanismer til at gentage transmission flere gange, kaldes Automatic Repeat ReQuest (ARQ) protokoller.
Derudover er det værd at overveje muligheden for fejl i kvitteringer, når den modtagende part ikke vil modtage nogen information om resultaterne af overførslen af ​​den sidste pakke.
Løsningen på dette problem, som også bruges i TCP, er at tilføje et nyt felt til datapakken, der indeholder pakkens sekvensnummer.

Grundlæggende om pålidelig dataoverførsel

Pålidelig datatransmission over en upålidelig kanal med forbehold for pakkeforvrængning og tab

Sammen med forvrængning er der desværre pakketab i netværket.
Og for at løse dette problem kræves mekanismer:

  • bestemmelse af pakketab;
  • genlevering af mistede pakker til den modtagende part.

Derudover er det, ud over tabet af pakken, nødvendigt at sørge for muligheden for tab af kvitteringen eller, hvis intet går tabt, leveringen med en betydelig forsinkelse. I alle tilfælde gøres det samme: pakken gentransmitteres. For at styre tiden bruger denne mekanisme en nedtællingstimer, som giver dig mulighed for at bestemme slutningen af ​​venteintervallet. Altså i pakken netto TCPKeepAlive er som standard indstillet til 15 sekunder:

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

Afsendersiden skal starte en timer hver gang en pakke sendes (både første og anden gang), håndtere afbrydelser fra timeren og stoppe den.

Så vi er blevet fortrolige med nøglebegreberne for pålidelige dataoverførselsprotokoller:

  • kontrolsummer;
  • sekvensnumre på pakker;
  • timere;
  • positive og negative kvitteringer.

Men det er ikke alt!

Pålidelig dataoverførselsprotokol med pipelining

I den variant, som vi allerede har overvejet, er den pålidelige leveringsprotokol meget ineffektiv. Det begynder at "sænke" transmissionen fra kommunikationskanalen, efterhånden som RTT stiger. For at øge dens effektivitet og bedre udnytte kommunikationskanalkapaciteten anvendes pipelining.

Grundlæggende om pålidelig dataoverførsel

Brugen af ​​pipelining fører til:

  • forøgelse af rækken af ​​sekvensnumre, da alle sendte pakker (undtagen gentransmissioner) skal identificeres entydigt;
  • behovet for at øge buffere på sende- og modtagesiden.

Kravene til sekvensnummerområdet og bufferstørrelsen afhænger af de handlinger, protokollen udfører som reaktion på pakkekorruption, -tab og -forsinkelse. I tilfælde af pipelining er der to metoder til at rette fejl:

  • returnere N pakker tilbage;
  • selektiv gentagelse.

Går tilbage N pakker - glidende vinduesprotokol

Grundlæggende om pålidelig dataoverførsel

Afsenderen skal understøtte tre typer begivenheder:

  • opkald med en protokol på højere niveau. Når dataafsendelsesfunktionen kaldes "ovenfra", kontrollerer afsendersiden først udfyldningsgraden af ​​vinduet (det vil sige tilstedeværelsen af ​​N sendte beskeder, der afventer modtagelse af kvitteringer). Hvis vinduet er tomt, genereres og transmitteres en ny pakke, og variabelværdierne opdateres. Ellers returnerer afsendersiden data til det øverste lag, og dette er en implicit indikation af, at vinduet er fuldt. Typisk vil det øverste lag forsøge at overføre dataene igen efter nogen tid. I en rigtig applikation vil afsenderen sandsynligvis enten buffere dataene (i stedet for at sende dem med det samme) eller have en synkroniseringsmekanisme (såsom en semafor eller flag), der vil tillade det øverste lag kun at kalde sendefunktionen, når vinduet er tomt .
  • modtager bekræftelse. I protokollen, for en pakke med sekvensnummer N, udstedes en generel bekræftelse, der indikerer, at alle pakker med sekvensnumre forud for N blev modtaget med succes.
  • venteintervallet er udløbet. For at bestemme fakta om tab og forsinkelser af pakker og kvitteringer, bruger protokollen en timer. Hvis timeout-intervallet udløber, sender afsendersiden alle sendte ikke-bekræftede pakker igen.

Selektiv gentagelse

Når vinduesstørrelsen og gennemløbs-udbredelsesforsinkelsesproduktet er store, kan et stort antal pakker være i pipelinen. I et sådant tilfælde kan en enkelt pakkefejl forårsage, at et stort antal pakker bliver gentransmitteret, hvoraf de fleste ikke var påkrævet.

Eksempel

Top teoretisk praksis er samlet i praktisk implementering TCP. Og hvis nogen ved hvordan det er bedst - velkommen.

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

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

Output

Mekanismer til at sikre pålidelig dataoverførsel og brug

mekanisme
Ansøgning, kommentar

Tjek sum
Bruges til at detektere bitfejl i en transmitteret pakke

timer
Tæller timeout-intervallet ned og angiver, hvornår det er udløbet. Sidstnævnte betyder, at med en høj grad af sandsynlighed går pakken eller dens kvittering tabt under transmissionen. Hvis en pakke leveres med forsinkelse, men ikke går tabt (for tidligt udløb af timeout-intervallet), eller en kvittering går tabt, fører retransmission til en duplikatpakke på modtagersiden

Serienummer
Bruges til sekventiel nummerering af datapakker transmitteret fra afsender til modtager. Huller i sekvensnumrene for modtagne pakker gør det muligt for modtageren at detektere pakketab. De samme pakkesekvensnumre betyder, at pakkerne er dubletter af hinanden

Bekræftelse
Genereret af den modtagende ende og angiver til afsenderenden, at den tilsvarende pakke eller gruppe af pakker er blevet modtaget. Typisk indeholder bekræftelsen sekvensnumrene for vellykket modtagne pakker. Afhængigt af protokollen skelnes individuelle og gruppebekræftelser

Negativ bekræftelse
Bruges af modtageren til at informere afsenderen om, at pakken er modtaget forkert. En negativ bekræftelse inkluderer normalt sekvensnummeret på den pakke, der ikke blev modtaget korrekt

Vindue, transportør
Begræns rækken af ​​sekvensnumre, der kan bruges til at sende pakker. Multicast og håndtryk kan øge protokolgennemstrømningen markant sammenlignet med at vente på bekræftelser. Som vi vil se, kan vinduesstørrelsen beregnes baseret på modtage- og bufferfunktionerne i den modtagende ende såvel som netværksbelastningsniveauet

Flere eksempler på brug af Go til netværk

В depoter.

Kilde: www.habr.com

Tilføj en kommentar