Luotettavan tiedonsiirron perusteet

Luotettavan tiedonsiirron perusteet

Niille, jotka etsii Omistettu verkkojen ja protokollien ymmärtämiseen.

lyhyesti

Artikkelissa käsitellään luotettavan tiedonsiirron perusteita ja esitetään esimerkkejä siitä, Go, mukaan lukien UDP ja TCP. Perustuu aika, два, kolme ja kirja "Tietokoneverkot: Ylhäältä alas -lähestymistapa", muuten kaikki keskustelevat vain Tannenbaumista ja Oliferovista.

Siirtokerroksen protokolla

Tarjoaa loogisen yhteyden eri isäntäkoneilla suoritettavien sovellusprosessien välille. Sovellusten näkökulmasta looginen yhteys näkyy kanavana, joka yhdistää prosesseja suoraan.

Luotettavan tiedonsiirron perusteet

Transport Layer -protokollat päätejärjestelmien tukema, mutta ei verkkoreitittimien (paitsi - DPILähettäjän puolella siirtokerros muuntaa lähettävältä sovellusprosessilta vastaanottamansa sovelluskerroksen tiedot siirtokerroksen paketeiksi, joita kutsutaan segmenteiksi.

Luotettavan tiedonsiirron perusteet

Tämä tehdään jakamalla (tarvittaessa) sovelluskerroksen viestit fragmentteihin ja lisäämällä siirtokerroksen otsikko jokaiseen fragmenttiin.

Luotettavan tiedonsiirron perusteet

Siirtokerros välittää sitten segmentin lähettäjän verkkokerrokselle, jossa se kapseloidaan verkkokerroksen pakettiin (datagrammiin) ja lähetetään. Vastaanottavassa päässä verkkokerros poimii siirtokerroksen segmentin datagrammista ja välittää sen siirtokerrokselle. Siirtokerros käsittelee sitten vastaanotetun segmentin niin, että sen tiedot ovat vastaanottavan sovelluksen käytettävissä.

Luotettavan tiedonsiirron perusteet

Turvallisen tiedonsiirron periaatteet

Luotettava tiedonsiirto täysin suojatun kanavan kautta

Yksinkertaisin tapaus: Lähettävä puoli yksinkertaisesti vastaanottaa dataa ylemmältä kerrokselta, luo sitä sisältävän paketin ja lähettää sen kanavaa pitkin.

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

asiakas

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

Luotettava tiedonsiirto kanavan yli, jossa on mahdollisia virheitä

Seuraava vaihe on olettaa, että kaikki lähetetyt paketit vastaanotetaan lähetysjärjestyksessä, mutta niiden bitit voivat olla vioittuneet, koska kanava lähettää joskus vääristyneitä tietoja.

Luotettavan tiedonsiirron perusteet

Tässä tapauksessa käytetään seuraavia mekanismeja:

  • virheiden havaitseminen;
  • palaute;
  • uudelleenlähetys.

Luotettavia tiedonsiirtoprotokollia, joilla on tällaiset mekanismit useille lähetystoistoille, kutsutaan Automatic Repeat ReQuest (ARQ) -protokolliksi.
Lisäksi kannattaa ottaa huomioon kuittien virheiden mahdollisuus, kun vastaanottava osapuoli ei saa mitään tietoa viimeisimmän paketin siirron tuloksista.
Tämän ongelman ratkaisu, jota käytetään muun muassa TCP:ssä, on lisätä datapakettiin uusi kenttä, joka sisältää paketin järjestysnumeron.

Luotettavan tiedonsiirron perusteet

Luotettava tiedonsiirto epäluotettavan kanavan kautta, joka mahdollistaa pakettien vääristymisen ja katoamisen

Vääristymien ohella verkossa valitettavasti esiintyy pakettien katoamista.
Ja tämän ongelman ratkaisemiseksi tarvitaan mekanismeja:

  • pakettien katoamisen tosiasian määrittäminen;
  • kadonneiden pakettien uudelleentoimitus vastaanottajalle.

Pakettihävikin lisäksi on otettava huomioon myös kuitin katoamisen mahdollisuus tai, jos mitään ei ole kadonnut, sen toimittaminen merkittävällä viiveellä. Kaikissa tapauksissa tapahtuu sama toimenpide: paketti lähetetään uudelleen. Ajoituksen hallitsemiseksi tämä mekanismi käyttää lähtölaskenta-ajastinta, jonka avulla voidaan määrittää odotusajan loppu. Näin ollen paketissa netto TCPKeepAlive on oletusarvoisesti asetettu arvoon 15 sekuntia:

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

Lähettävän puolen on käynnistettävä ajastin joka kerta, kun paketti lähetetään (sekä ensimmäisen että toisen kerran), käsiteltävä ajastimen keskeytykset ja pysäytettävä se.

Olemme siis tutustuneet luotettavien tiedonsiirtoprotokollien keskeisiin käsitteisiin:

  • tarkistussummat;
  • pakkausten sarjanumerot;
  • ajastimet;
  • positiiviset ja negatiiviset tulot.

Mutta siinä ei ole kaikki!

Luotettava tiedonsiirtoprotokolla putkistolla

Edellä tarkastelemassamme toteutuksessa luotettava toimitusprotokolla on erittäin tehoton. Se alkaa "hidastaa" tietoliikennekanavan tarjoamaa lähetystä RTT:n kasvaessa. Tehokkuuden parantamiseksi ja tietoliikennekanavan kaistanleveyden hyödyntämiseksi käytetään liukuhihnatekniikkaa (pipelining).

Luotettavan tiedonsiirron perusteet

Putkiston käyttö johtaa:

  • järjestysnumeroiden alueen lisääminen, koska kaikkien lähetettyjen pakettien (uudelleenlähetyksiä lukuun ottamatta) on oltava yksilöllisesti tunnistettavissa;
  • tarve lisätä puskureita lähetys- ja vastaanottopuolella.

Sarjanumeroiden vaihteluväli ja puskurin kokovaatimukset riippuvat protokollan reaktiosta pakettien vioittumiseen, katoamiseen ja viiveeseen. Liukuhihnakäytössä on kaksi virheenkorjausmenetelmää:

  • palauta N pakettia takaisin;
  • valikoiva toisto.

Taaksepäin N pakettia - liukuvan ikkunan protokolla

Luotettavan tiedonsiirron perusteet

Lähettäjän on tuettava kolmenlaisia ​​tapahtumia:

  • Ylemmän tason protokollan kutsu. Kun lähetysfunktiota kutsutaan ylhäältä, lähettäjä tarkistaa ensin, onko ikkuna täynnä (eli onko N lähetettyä viestiä odottamassa kuittausta). Jos ikkuna ei ole täynnä, muodostetaan ja lähetetään uusi paketti, ja muuttujien arvot päivitetään. Muussa tapauksessa lähettäjä palauttaa tiedot ylemmälle kerrokselle, mikä on implisiittinen osoitus siitä, että ikkuna on täynnä. Tyypillisesti ylempi kerros yrittää tiedon lähettämistä uudelleen jonkin ajan kuluttua. Todellisessa sovelluksessa lähettäjä todennäköisesti joko puskuroisi tiedot (sen sijaan, että lähettäisi ne välittömästi) tai sillä olisi synkronointimekanismi (esim. semafori tai lippu), joka sallisi ylemmän kerroksen kutsua lähetysfunktiota vain, kun ikkuna ei ole täynnä.
  • Kuittauksen vastaanottaminen. Protokollassa annetaan yleinen kuittaus järjestysnumeroltaan N olevalle paketille, joka osoittaa, että kaikki N:ää edeltävät järjestysnumerot sisältävät paketit on vastaanotettu onnistuneesti.
  • Aikakatkaisun umpeutuminen. Protokolla käyttää ajastinta pakettien ja kuittausten katoamisen ja viiveiden havaitsemiseen. Jos aikakatkaisu umpeutuu, lähettävä osapuoli lähettää uudelleen kaikki kuittaamattomat paketit.

Valikoiva toisto

Kun ikkunan koko ja läpäisykyvyn ja viiveen tulo ovat suuria, putkessa voi olla suuri määrä paketteja. Tässä tapauksessa yhden paketin virhe voi aiheuttaa suuren määrän pakettien uudelleenlähetyksen, joista useimpia ei tarvittu.

Esimerkki

Toppi teoreettinen käytäntöjä kerätään käytännön toteutuksessa TCPJa jos joku tietää paremman tavan... tervetuloa.

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

asiakas

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

johtopäätös

Mekanismit, jotka varmistavat luotettavan tiedonsiirron ja käytön

Механизм
Sovellus, kommentti

Tarkista summa
Käytetään lähetetyn paketin bittivirheiden havaitsemiseen.

ajastin
Aikakatkaisuvälin lähtölaskenta ja sen umpeutumisen ilmaisin. Jälkimmäinen osoittaa, että paketti tai sen kuittaus on suurella todennäköisyydellä kadonnut lähetyksen aikana. Jos paketti toimitetaan myöhässä, mutta ei ole kadonnut (aikakatkaisuvälin ennenaikainen umpeutuminen) tai kuittaus katoaa, uudelleenlähetys johtaa paketin kaksoiskappaleeseen vastaanottavassa päässä.

Sarjanumero
Käytetään lähettäjältä vastaanottajalle lähetettyjen datapakettien järjestysnumerointiin. Vastaanotettujen pakettien järjestysnumeroiden aukot mahdollistavat vastaanottajan pakettien katoamisen havaitsemisen. Identtiset pakettien järjestysnumerot osoittavat kaksoispaketteja.

vahvistus
Vastaanottava osapuoli luo tämän kuitin, joka ilmoittaa lähettävälle osapuolelle, että vastaava paketti tai pakettiryhmä on vastaanotettu onnistuneesti. Kuittaukset sisältävät tyypillisesti onnistuneesti vastaanotettujen pakettien järjestysnumerot. Protokollasta riippuen erotetaan toisistaan ​​yksittäiset ja ryhmäkuittaukset.

Negatiivinen vahvistus
Vastaanottaja käyttää tätä ilmoittaakseen lähettäjälle, että pakettia ei vastaanotettu oikein. Negatiivinen kuittaus sisältää tyypillisesti sen paketin järjestysnumeron, jota ei vastaanotettu oikein.

Ikkuna, putkisto
Ne rajoittavat pakettien lähetyksessä käytettävien järjestysnumeroiden valikoimaa. Monilähetys ja kättely lisäävät merkittävästi protokollan läpäisykykyä verrattuna kuittausten odottamiseen. Kuten näemme, ikkunan koko voidaan laskea vastaanottavan pään vastaanotto- ja puskurointiominaisuuksien sekä verkon kuormituksen perusteella.

Muita esimerkkejä Go:n käytöstä verkostoitumiseen

В arkistot.

Lähde: will.com

Lisää kommentti