Fundamentals fan betroubere gegevensoerdracht

Fundamentals fan betroubere gegevensoerdracht

Oan dyjingen dy't siket Tawijd oan it begripen fan netwurken en protokollen.

Koartsein

It artikel besprekt de basis fan betroubere gegevens oerdracht, ymplemintearret foarbylden op Go, ynklusyf UDP en TCP. Basearre op kearen, два, trije en de boeken "Computer Networks. Top-Down Approach", oars besprekt elkenien allinich Tannenbaum en Oliferov.

Transport laach protokol

Biedt in logyske ferbining tusken applikaasjeprosessen dy't rinne op ferskate hosts. Fanút in applikaasjeperspektyf liket in logyske ferbining op in kanaal dat prosessen direkt ferbynt.

Fundamentals fan betroubere gegevensoerdracht

Ferfier laach protokollen wurde stipe troch einsystemen, mar net troch netwurkrouters (útsein - DPI). Oan 'e stjoerderkant konvertearret de transportlaach de gegevens fan' e applikaasjelaach dy't it ûntfangt fan it ferstjoerende applikaasjeproses yn transportlaachpakketten neamd segminten.

Fundamentals fan betroubere gegevensoerdracht

Dit wurdt dien troch te splitsen (as nedich) de applikaasje laach berjochten yn fragminten en it tafoegjen fan in transportlaach header oan elk fan harren.

Fundamentals fan betroubere gegevensoerdracht

It ferfier laach dan giet it segmint nei de stjoerder syn netwurk laach, dêr't it segmint wurdt ynkapsele yn in netwurk laach pakket (datagram) en ferstjoerd. Oan it ûntfangende ein ekstrakt de netwurklaach it transportlaachsegment út it datagram en jout it troch nei de transportlaach. Dêrnei ferwurket de ferfierslaach it ûntfongen segmint sadat syn gegevens beskikber wurde foar de ûntfangende applikaasje.

Fundamentals fan betroubere gegevensoerdracht

Prinsipes fan betroubere gegevensoerdracht

Betrouwbare gegevensoerdracht oer in folslein feilich kanaal

It ienfâldichste gefal. De ferstjoerside ûntfangt gewoan de gegevens fan 'e boppeste laach, makket in pakket dat it befettet en stjoert it nei it kanaal.

Tsjinner

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 gegevensoerdracht oer in kanaal mei mooglike flaters

De folgjende stap is om oan te nimmen dat alle ferstjoerde pakketten wurde ûntfongen yn 'e folchoarder wêryn't se waarden ferstjoerd, mar de bits yn har kinne beskeadige wurde troch it feit dat it kanaal soms gegevens mei ferfoarmings ferstjoert.

Fundamentals fan betroubere gegevensoerdracht

Yn dit gefal wurde de folgjende meganismen brûkt:

  • flaterdeteksje;
  • feedback;
  • retransmission.

Betroubere protokollen foar oerdracht fan gegevens dy't ferlykbere meganismen hawwe foar it werheljen fan oerdracht meardere kearen wurde neamd Automatic Repeat reQuest (ARQ) protokollen.
Dêrneist is it de muoite wurdich om te beskôgje de mooglikheid fan flaters yn ûntfangst, doe't de ûntfangende partij sil krije gjin ynformaasje oer de resultaten fan de oerdracht fan it lêste pakket.
De oplossing foar dit probleem, ek brûkt yn TCP, is om in nij fjild ta te foegjen oan it gegevenspakket mei it folchoardernûmer fan it pakket.

Fundamentals fan betroubere gegevensoerdracht

Betrouwbare gegevensoerdracht oer in ûnbetrouber kanaal ûnder foarbehâld fan pakketferfoarming en ferlies

Tegearre mei ferfoarming is d'r spitigernôch pakketferlies yn it netwurk.
En om dit probleem op te lossen binne meganismen nedich:

  • it fêststellen fan it feit fan pakketferlies;
  • opnij levering fan ferlerne pakketten oan de ûntfangende partij.

Derneist, neist it ferlies fan it pakket, is it nedich om te soargjen foar de mooglikheid fan ferlies fan 'e ûntfangst of, as neat is ferlern, syn levering mei in signifikante fertraging. Yn alle gefallen wurdt itselde dien: it pakket wurdt opnij ferstjoerd. Om de tiid te kontrolearjen, brûkt dit meganisme in countdown-timer, wêrtroch jo it ein fan it wachtynterval kinne bepale. Yn it pakket dus net TCPKeepAlive is standert ynsteld op 15 sekonden:

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

De stjoerder moat elke kear in timer begjinne as in pakket wurdt ferstjoerd (sawol de earste as de twadde kear), ûnderbrekkings fan 'e timer behannelje en it stopje.

Dat, wy binne bekend wurden mei de kaaibegripen fan betroubere protokollen foar gegevensferfier:

  • checksums;
  • folchoarder nûmers fan pakketten;
  • timers;
  • positive en negative ûntfangsten.

Mar dat is net alles!

Betrouber protokol foar gegevensoerdracht mei pipelining

Yn 'e fariant dy't wy al hawwe beskôge, is it betroubere leveringsprotokol heul yneffisjint. It begjint de oerdracht fan it kommunikaasjekanaal te "fertrage" as de RTT ferheget. Om de effisjinsje te fergrutsjen en de kapasiteit fan 'e kommunikaasjekanaal better te brûken, wurdt pipelining brûkt.

Fundamentals fan betroubere gegevensoerdracht

It brûken fan pipelining liedt ta:

  • it fergrutsjen fan it berik fan folchoardernûmers, om't alle ferstjoerde pakketten (útsein foar retransmissions) unyk identifisearre wurde moatte;
  • de needsaak om buffers te fergrutsjen oan 'e stjoerende en ûntfangende kanten.

De easken foar folchoardernûmerberik en buffergrutte binne ôfhinklik fan 'e aksjes dy't it protokol nimt yn reaksje op pakketkorrupsje, ferlies en fertraging. Yn it gefal fan pipelining binne d'r twa metoaden foar it korrigearjen fan flaters:

  • werom N pakketten werom;
  • selektive werhelling.

Gean werom N pakketten - sliding finster protokol

Fundamentals fan betroubere gegevensoerdracht

De stjoerder moat trije soarten eveneminten stypje:

  • oprop troch in heger nivo protokol. As de gegevensferstjoerfunksje "fan boppe" neamd wurdt, kontrolearret de ferstjoerside earst de mjitte fan it filling fan it finster (dat is de oanwêzigens fan N ferstjoerde berjochten dy't wachtsje op ûntfangst fan ûntfangst). As it finster leech is, wurdt in nij pakket oanmakke en ferstjoerd, en de fariabele wearden wurde bywurke. Oars jout de stjoerende kant gegevens werom nei de boppeste laach, en dit is in ymplisite oantsjutting dat it finster fol is. Typysk sil de boppeste laach besykje de gegevens nei in skoftke opnij te ferstjoeren. Yn in echte applikaasje soe de stjoerder wierskynlik de gegevens bufferje (ynstee fan it fuortdaliks te ferstjoeren) of in syngronisaasjemeganisme hawwe (lykas in semafoar of flagge) wêrtroch de boppeste laach de ferstjoerfunksje allinich kin neame as it finster leech is .
  • ûntfange befêstiging. Yn it protokol wurdt foar in pakket mei folchoardernûmer N in algemiene erkenning útjûn dy't oanjout dat alle pakketten mei folchoardernûmers foarôfgeand oan N mei sukses ûntfongen binne.
  • it wachtinterval is ferrûn. Om de feiten fan ferliezen en fertragingen fan pakketten en ûntfangsten te bepalen, brûkt it protokol in timer. As it timeout-ynterval ferrint, stjoert de ferstjoerende side alle ferstjoerde net-erkende pakketten opnij.

Selektive werhelling

As de finstergrutte en it produkt foar fertraging fan trochset-propagaasje grut binne, kin in grut oantal pakketten yn 'e pipeline wêze. Yn sa'n gefal kin in inkele pakketflater feroarsaakje dat in grut oantal pakketten opnij wurde ferstjoerd, wêrfan de measten net nedich wiene.

Foarbyld:

De bêste teoretyske praktiken wurde sammele yn praktyske ymplemintaasje TCP. En as immen wit hoe it bêste - Wolkom.

Tsjinner

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

konklúzje

Mechanismen om betroubere gegevensferfier en gebrûk te garandearjen

Mechanisme
Applikaasje, kommentaar

Kontrolearje som
Wurdt brûkt om bitflaters te detektearjen yn in ferstjoerd pakket

Timer
Telt it timeout-ynterval ôf en jout oan wannear't it ferrûn is. Dat lêste betsjut dat mei in hege graad fan kâns it pakket of syn ûntfangst is ferlern by it oerdracht. As in pakket wurdt levere mei in fertraging, mar is net ferlern (foar betiid ferrinnen fan it timeout ynterval), of in ûntfangst is ferlern, retransmission liedt ta in duplikaat pakket oan de ûntfangende kant

Searynûmer
Wurdt brûkt foar sekwinsjele nûmering fan gegevenspakketten oerstjoerd fan stjoerder nei ûntfanger. Lappen yn 'e folchoardernûmers fan ûntfongen pakketten kinne de ûntfanger pakketferlies detektearje. Deselde pakketsekwinsjenûmers betsjutte dat de pakketten duplikaten fan elkoar binne

Befêstiging
Generearre troch it ûntfangende ein en oanjout oan it ferstjoerende ein dat it oerienkommende pakket of groep pakketten mei súkses ûntfongen is. Typysk befettet de erkenning de folchoardernûmers fan suksesfol ûntfongen pakketten. Ofhinklik fan it protokol wurde yndividuele en groepsbefestigingen ûnderskieden

Negative befêstiging
Wurdt brûkt troch de ûntfanger om de stjoerder te ynformearjen dat it pakket ferkeard ûntfongen is. In negative erkenning omfettet normaal it folchoardernûmer fan it pakket dat net goed ûntfongen is

Finster, conveyorization
Beheine it berik fan folchoardernûmers dat kin wurde brûkt om pakketten te ferstjoeren. Multicast en handshake kinne protokol-trochput signifikant ferheegje yn ferliking mei wachtsjen op erkenningen. Lykas wy sille sjen, kin de finstergrutte wurde berekkene op basis fan 'e ûntfangst- en buffermooglikheden fan' e ûntfangende ein, lykas it nivo fan netwurklast

Mear foarbylden fan it brûken fan Go foar netwurken

В repositories.

Boarne: www.habr.com

Add a comment