Základy spolehlivého přenosu dat

Základy spolehlivého přenosu dat

Těm, kdo se snaží rozumí sítím a protokolům, je tento text věnován.

Stručně

Článek se zabývá základy spolehlivého přenosu dat a uvádí příklady na Go, včetně UDP a TCP. Na motivy jedna, dva, tři a knihy "Počítačové sítě. Sestupný přístup", protože všichni diskutují jen o Tanenbaumovi a Oliferové.

Protokol transportní úrovně

zajišťuje logické spojení mezi aplikačními procesy, které běží na různých hostitelích. Z pohledu aplikací vypadá logické spojení jako kanál, který přímo spojuje procesy.

Základy spolehlivého přenosu dat

Protokoly transportní úrovně jsou podporovány koncovými systémy, ale ne síťovými směrovači (kromě - DPI). Na straně odesílatele transportní úroveň převádí data aplikační úrovně, která přijímá od odesílajícího aplikačního procesu, na pakety transportní úrovně zvané segmenty.

Základy spolehlivého přenosu dat

To se provádí rozdělením (pokud je to nutné) zpráv aplikační úrovně na fragmenty a přidáním hlavičky transportní úrovně ke každému z nich.

Základy spolehlivého přenosu dat

Dále transportní úroveň předává segment síťové úrovni odesílatele, kde je segment zapouzdřen do paketu síťové úrovně (datagramu) a odeslán. Na přijímající straně síťová úroveň extrahuje segment transportní úrovně z datagramu a předává jej zpět transportní úrovni. Následně transportní úroveň zpracovává přijatý segment tak, aby jeho data byla přístupná cílové aplikaci.

Základy spolehlivého přenosu dat

Principy spolehlivého přenosu dat

Spolehlivý přenos dat přes zcela spolehlivý kanál

Nejjednodušší případ. Odesílající strana jednoduše přijímá data z vyšší úrovně, vytváří balíček, který je obsahuje, a odesílá jej do kanálu.

Server

package main

import (
    "log"
    "net"
)

func main() {
    // IP adresa serveru a port
    serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
    if err != nil {
        log.Fatal(err)
    }

    // vytvoření socketu na portu
    serverConn, err := net.ListenUDP("udp", serverAddr)
    if err != nil {
        log.Fatal(err)
    }
    // odložené uzavření spojení
    defer serverConn.Close()

    // vytvoření bufferu pro data
    buf := make([]byte, 1024)

    // čekání na spojení
    for {
        // čtení požadavku
        n, addr, err := serverConn.ReadFromUDP(buf)
        // předání dat na VĚTŠÍ úroveň: v našem případě stdout
        println(string(buf[0:n]), " form ", addr.IP.String())
        if err != nil {
            log.Fatal(err)
        }
        // odpověď není, protože jde o UDP + spolehlivý kanál
    }
}

Zákazník

package main

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

func main() {
    // IP adresa serveru a port
    serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
    if err != nil {
        log.Fatal(err)
    }
    // lokální IP adresa a port
    localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
    if err != nil {
        log.Fatal(err)
    }
    // nastavení spojení
    conn, err := net.DialUDP("udp", localAddr, serverAddr)
    if err != nil {
        log.Fatal(err)
    }
    // odložené uzavření spojení
    defer conn.Close()

    for {
        // příjem dat z VĚTŠÍ úrovně
        fmt.Print("Zadejte řetězec > ")
        var msg string
        _, err := fmt.Scanf("%s", &msg)
        if err != nil {
            log.Fatal(err)
        }
        // přenáší se proud bytů, nikoliv řetězec
        buf := []byte(msg)
        // zápis (přenášení) do spojení
        _, err = conn.Write(buf)
        if err != nil {
            log.Fatal(err)
        }
        // 1 vteřina
        time.Sleep(time.Second * 1)
    }
}

Spolehlivý přenos dat přes kanál s možnými chybami

Dalším krokem je předpoklad, že všechny přenášené pakety byly přijaty v pořadí, v jakém byly odeslány, ale bity v nich mohou být poškozeny, protože kanál občas přenáší data s deformacemi.

Základy spolehlivého přenosu dat

V takovém případě se aplikují mechanismy:

  • detekce chyb;
  • zpětná vazba;
  • opakované přenosy.

Protokoly pro spolehlivý přenos dat, které mají podobné mechanismy opakovaného přenosu, se nazývají protokoly s automatickou žádostí o opakovaný přenos (Automatic Repeat reQuest, ARQ).
Dále je dobré zohlednit možnost chyb i v potvrzeních, kdy přijímající strana nedostane žádné informace o výsledcích přenosu posledního paketu.
Řešení tohoto úkolu, používané včetně TCP, spočívá v přidání nového pole pro sekvenční číslo paketu do datového paketu.

Základy spolehlivého přenosu dat

Spolehlivý přenos dat přes nespolehlivý kanál, který může způsobit ztrátu a zkreslení paketů

Bohužel, při zkreslení dochází také ke ztrátě paketů.
A pro vyřešení tohoto problému jsou zapotřebí mechanismy:

  • určení ztráty paketu;
  • opětovné odeslání ztracených paketů přijímající straně.

Dále je třeba kromě ztráty paketu zohlednit možnost ztráty potvrzení nebo, pokud nebylo nic ztraceno, jeho doručení se značným zpožděním. V každém případě se provádí to samé: opětovné odeslání paketu. Pro sledování času se v tomto mechanismu používá odpočítávací časovač, který umožňuje určit konec čekacího intervalu. Tak v paketu net parametr TCPKeepAlive je ve výchozím nastavení nastaven na 15 sekund:

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

Odesílající straně je třeba spustit časovač pokaždé při odesílání paketu (jak při prvním, tak při opakovaném odeslání), zpracovávat přerušení od časovače a zastavit jej.

Tak jsme se seznámili s klíčovými pojmy protokolů spolehlivého přenosu dat:

  • kontrolními součty;
  • pořadovými čísly paketů;
  • časovači;
  • kladnými a zápornými potvrzeními.

Ale to není všechno!

Protokol spolehlivého přenosu dat s pipeliningem

V variantě, kterou jsme již probrali, je protokol spolehlivé dopravy velmi neefektivní. Při zvyšující se RTT začíná 'brzdit' přenos zajišťovaný komunikačním kanálem. Pro zvýšení jeho efektivity, lepšího využití šířky pásma komunikačního kanálu se používá pipelining.

Základy spolehlivého přenosu dat

Použití pipeliningu vede k:

  • zvýšení rozsahu pořadových čísel, protože všechny odesílané pakety (kromě opakovaných) musí být jednoznačně identifikovatelné;
  • potřebě větších vyrovnávacích pamětí na odesílající a přijímající straně.

Rozsah pořadových čísel a požadavky na velikosti vyrovnávacích pamětí závisí na akcích, které protokol podniká na základě zkreslení, ztráty a zpoždění paketu. V případě pipeliningu existují dvě metody opravy chyb:

  • návrat o N paketů zpět;
  • výběrové opakování.

Návrat o N paketů zpět — protokol klouzavého okna

Základy spolehlivého přenosu dat

odesílatel musí sledovat tři typy událostí:

  • Volání protokolem vyšší úrovně. Když je funkce odesílání dat volána „zhora“, odesílající strana nejprve zkontroluje stupeň zaplnění okna (to znamená přítomnost N odeslaných zpráv čekajících na potvrzení). Pokud se okno ukáže jako nezaplněné, je vytvořen a odeslán nový paket a hodnoty proměnných jsou aktualizovány. V opačném případě odesílající strana vrací data vyšší úrovni, což je implicitní pokyn, že je okno zaplněno. Obvykle se vyšší úroveň pokouší znovu odeslat data po nějakém čase. V reálné aplikaci by odesílatel pravděpodobně buď data vyrovnal (místo okamžitého odesílání), nebo měl mechanismus synchronizace (například semafor nebo příznak), který by umožnil vyšší úrovni volat funkci odesílání dat pouze při nezaplněném okně.
  • Potvrzení přijetí. V protokolu je pro paket s pořadovým číslem N vydávána obecná potvrzení, která ukazují, že všechny pakety s pořadovými čísly předcházejícími N byly úspěšně přijaty.
  • Vypršení časového intervalu. Pro stanovení ztrát a zpoždění paketů a potvrzení protokol používá časovač. Pokud časový interval vyprší, odesílající strana znovu odešle všechny odeslané nepotvrzené pakety.

Výběrové opakování

Když je velikost okna a produkt propustnosti s prodlevou vysoký, může být v potrubí velké množství paketů. V takovém případě chyba jednotlivého paketu může způsobit znovuodeslání velkého množství paketů, z nichž většina nebyla potřebná.

Příklad

Nejlepší teoretické praktiky jsou shromážděny v praktické implementaci TCP. A pokud někdo ví, jak lépe — vítejte.

Server

package main

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

func main() {
    // vytváříme socket s portem 
    ln, err := net.Listen("tcp", ":8081")
    if err != nil {
        log.Fatalln(err)
    }
    // čekání na volání
    conn, _ := ln.Accept()

    for {
        // čtení dat
        msg, err := bufio.NewReader(conn).ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // výpis zprávy na stdout
        fmt.Print("Přijata zpráva:", string(msg))
        // převod řetězce na velká písmena
        newMsg := strings.ToUpper(msg)
        // odesílání dat
        conn.Write([]byte(newMsg + "n"))
    }
}

Zákazník

balíček hlavní

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

funkce main() {
    // nastavení spojení
    conn, err := net.Dial("tcp", "127.0.0.1:8081")
    if err != nil {
        log.Fatalln(err)
    }

    for {
        // čtení dat z stdin
        reader := bufio.NewReader(os.Stdin)
        fmt.Print("Text k odeslání: ")
        // po řádcích
        text, err := reader.ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // odeslání
        fmt.Fprintf(conn, text+"n")
        // příjem
        msg, err := bufio.NewReader(conn).ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // výstup přijaté odpovědi
        fmt.Print("Zpráva ze serveru: " + msg)
    }
}

Výstup

Mechanismy zajišťující spolehlivý přenos dat a jejich využití

Mechanismus
Aplikace, komentář

Kontrolní součet
Používá se k odhalení bitových chyb v přeneseném balíčku

Časovač
Odpočet čekacího intervalu a indikace jeho vypršení. To poslední znamená, že s vysokou pravděpodobností byl balíček nebo jeho potvrzení ztraceno během přenosu. Pokud je balíček dodán se zpožděním, ale není ztracen (předčasné vypršení čekacího intervalu), nebo dojde ke ztrátě potvrzení, opakovaný přenos vede k duplikaci balíčku na přijímající straně

Pořadové číslo
Používá se pro pořadovou numeraci datových balíčků přenášených od odesílatele k příjemci. Přerušení v pořadových číslech přijatých balíčků umožňují příjemci odhalit ztrátu balíčku. Stejná pořadová čísla balíčků znamenají, že balíčky se navzájem duplikují

Potvrzení
Generováno přijímající stranou a signalizuje odesílající straně, že příslušný balíček nebo skupina balíčků byla úspěšně přijata. Obvykle potvrzení obsahuje pořadová čísla úspěšně přijatých balíčků. V závislosti na protokolu se rozlišují individuální a skupinová potvrzení

Negativní potvrzení
Používá se příjemcem k oznámení odesílateli, že balíček byl přijat nesprávně. Negativní potvrzení obvykle obsahuje pořadové číslo balíčku, který nebyl správně přijat

Okno, konvejování
Omezuje rozsah pořadových čísel, která mohou být použita pro přenos paketů. Skupinový přenos a handshake výrazně zvyšují propustnost protokolů v porovnání s režimem čekání na potvrzení. Jak uvidíme, velikost okna může být vypočítána na základě schopností příjmu a vyrovnávací paměti přijímající strany, stejně jako úrovně zatížení sítě.

Další příklady použití Go pro práci se sítí

V repozitáře.

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster