Grundlæggende om pålidelig dataoverførsel

Grundlæggende om pålidelig dataoverførsel

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

Kort

Artiklen diskuterer det grundlæggende i pålidelig dataoverførsel og implementerer eksempler på Go, inklusive UDP og TCP. Baseret på tid, два, tre og bogen "Computer Networks. A 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 fremstår en logisk forbindelse som en kanal, der direkte forbinder processer.

Grundlæggende om pålidelig dataoverførsel

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

Grundlæggende om pålidelig dataoverførsel

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

Grundlæggende om pålidelig dataoverførsel

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

Grundlæggende om pålidelig dataoverførsel

Principper for sikker dataoverførsel

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

Det enkleste tilfælde. Afsendersiden modtager simpelthen data fra det øverste lag, opretter en pakke, der indeholder dem, og sender dem ned ad 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 dataoverførsel 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, men at bitsene i dem kan være ødelagte 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:

  • fejldetektion;
  • feedback;
  • videresendelse.

Pålidelige datatransmissionsprotokoller, der har sådanne mekanismer til flere gentagelser af transmissioner, kaldes ARQ-protokoller (Automatic Repeat reQuest).
Derudover er det værd at overveje muligheden for fejl i kvitteringer, når modtageren ikke modtager nogen information om resultaterne af overførslen af ​​den sidste pakke.
Løsningen på dette problem, som blandt andet 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 dataoverførsel over en upålidelig kanal, der tillader pakkeforvrængning og -tab

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

  • fastlæggelse af faktummet for pakketab;
  • omlevering af mistede pakker til modtageren.

Derudover er det, udover tabet af pakken, nødvendigt at tage højde for muligheden for at miste kvitteringen eller, hvis intet går tabt, at den leveres med en betydelig forsinkelse. I alle tilfælde sker det samme: pakken sendes videre. For at styre tiden bruger denne mekanisme en nedtællingstimer, som giver dig mulighed for at bestemme slutningen af ​​ventetiden. Så 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
)

Den sendende side skal starte timeren hver gang en pakke transmitteres (både første og anden gang), håndtere afbrydelser fra timeren og stoppe den.

Så er vi blevet bekendt med nøglebegreberne i pålidelige datatransmissionsprotokoller:

  • checksummer;
  • serienumre på pakker;
  • timere;
  • positive og negative indtægter.

Men det er ikke alt!

Pålidelig dataoverførselsprotokol med pipelining

I den version, vi allerede har overvejet, er den pålidelige leveringsprotokol meget ineffektiv. Den begynder at "bremse" transmissionen leveret af kommunikationskanalen, efterhånden som RTT stiger. For at øge effektiviteten og bedre udnytte kommunikationskanalens kapacitet anvendes pipelining.

Grundlæggende om pålidelig dataoverførsel

Brugen af ​​pipelining resulterer i:

  • øge intervallet af sekvensnumre, da alle sendte pakker (undtagen retransmissioner) skal være entydigt identificerbare;
  • behovet for at øge bufferne på sende- og modtagersiden.

Intervallet for sekvensnumre og krav til bufferstørrelse afhænger af de handlinger, som protokollen udfører som reaktion på pakkekorruption, tab og forsinkelse. I tilfælde af pipelining er der to metoder til fejlkorrektion:

  • returner N pakker tilbage;
  • selektiv gentagelse.

Baglæns N-pakker - glidende vinduesprotokol

Grundlæggende om pålidelig dataoverførsel

Afsenderen skal understøtte tre typer hændelser:

  • opkald via en protokol på højere niveau. Når funktionen "send data" kaldes "ovenfra", kontrollerer afsendersiden først vinduets fyldningsgrad (dvs. tilstedeværelsen af ​​N sendte beskeder, der venter på at modtage kvitteringer). Hvis vinduet ikke er udfyldt, dannes og transmitteres en ny pakke, og værdierne af variablerne opdateres. Ellers returnerer den sendende side 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 et stykke tid. I en rigtig applikation ville afsenderen sandsynligvis enten buffere dataene (i stedet for at sende dem med det samme) eller have en synkroniseringsmekanisme (såsom en semafor eller et flag), der ville tillade det højere lag kun at kalde send-funktionen, når vinduet ikke er fuldt.
  • modtager bekræftelse. I protokollen udstedes der en generel bekræftelse for en pakke med sekvensnummer N, der angiver, at alle pakker med sekvensnumre før N er modtaget.
  • timeout-perioden er udløbet. Protokollen bruger en timer til at detektere tab og forsinkelser af pakker og kvitteringer. Hvis timeout-intervallet udløber, sender den sendende side alle sendte, ubekræftede pakker igen.

Selektiv gentagelse

Når vinduesstørrelsen og gennemløbsforsinkelsesproduktet er stort, kan der være et stort antal pakker i pipelinen. I et sådant tilfælde kan en fejl i en enkelt pakke forårsage, at et stort antal pakker sendes igen, hvoraf de fleste ikke var nødvendige.

Eksempel

Top teoretisk praksis indsamles i den praktiske implementering TCP. Og hvis nogen ved bedre - 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, der sikrer pålidelig dataoverførsel og -brug

mekanisme
Ansøgning, kommentar

Kontrolsum
Bruges til at detektere bitfejl i en sendt pakke.

timer
Tæller ned for timeout-intervallet og angiver dets udløb. Sidstnævnte betyder, at der er stor sandsynlighed for, at pakken eller dens modtagelse gik tabt under transmissionen. Hvis en pakke leveres med en forsinkelse, men ikke går tabt (for tidlig udløb af timeout-intervallet), eller hvis modtagelsen går tabt, resulterer retransmission i en duplikatpakke på modtagersiden.

Serienummer
Bruges til sekventiel nummerering af datapakker, der sendes fra afsender til modtager. Huller i sekvensnumrene for modtagne pakker gør det muligt for modtageren at registrere pakketab. Identiske sekvensnumre for pakker betyder, at pakkerne er dubletter af hinanden.

Bekræftelse
Genereres af den modtagende part og indikerer til den sendende part, at den tilsvarende pakke eller gruppe af pakker er blevet modtaget. Typisk indeholder bekræftelsen sekvensnumrene på de pakker, der er modtaget. Afhængigt af protokollen skelnes der mellem individuelle og gruppebekræftelser.

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

Vindue, rørledning
Begræns området for sekvensnumre, der kan bruges til at sende pakker. Multicast- og handshake-transmissioner gør det muligt at øge protokollernes gennemløbshastighed betydeligt sammenlignet med at vente på bekræftelser. Som vi vil se, kan vinduesstørrelsen beregnes ud fra modtagerens modtage- og bufferkapacitet samt netværkets belastningsniveau.

Flere eksempler på Go Networking

В depoter.

Kilde: www.habr.com

Tilføj en kommentar