Luotettavan tiedonsiirron perusteet

Luotettavan tiedonsiirron perusteet

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

lyhyesti

Artikkelissa käsitellään luotettavan tiedonsiirron perusteita, toteutetaan esimerkkejä Go, mukaan lukien UDP ja TCP. Perustuen aika, два, kolme ja kirjat "Tietokoneverkot. Top-Down Approach", muuten kaikki keskustelevat vain Tannenbaumista ja Oliferovista.

Kuljetuskerroksen protokolla

Tarjoaa loogisen yhteyden eri isännillä käynnissä olevien sovellusprosessien välillä. Sovelluksen näkökulmasta looginen yhteys näyttää kanavalta, joka yhdistää suoraan prosesseja.

Luotettavan tiedonsiirron perusteet

Kuljetuskerroksen protokollat loppujärjestelmät tukevat niitä, mutta eivät verkkoreitittimet (paitsi - DPI). Lähettäjäpuolella kuljetuskerros muuntaa lähettävältä sovellusprosessilta vastaanottamansa sovelluskerroksen tiedot kuljetuskerroksen paketeiksi, joita kutsutaan segmenteiksi.

Luotettavan tiedonsiirron perusteet

Tämä tehdään jakamalla (tarvittaessa) sovelluskerroksen viestit fragmenteiksi ja lisäämällä jokaiseen niistä kuljetuskerroksen otsikko.

Luotettavan tiedonsiirron perusteet

Siirtokerros välittää sitten segmentin lähettäjän verkkokerrokseen, jossa segmentti kapseloidaan verkkokerroksen pakettiin (datagrammiin) ja lähetetään. Vastaanottopäässä verkkokerros poimii siirtokerroksen segmentin datagrammista ja välittää sen siirtokerrokselle. Seuraavaksi kuljetuskerros käsittelee vastaanotetun segmentin niin, että sen tiedot tulevat vastaanottavan sovelluksen saataville.

Luotettavan tiedonsiirron perusteet

Luotettavan tiedonsiirron periaatteet

Luotettava tiedonsiirto täysin suojatun kanavan kautta

Yksinkertaisin tapaus. Lähettävä puoli yksinkertaisesti vastaanottaa datan ylemmältä kerrokselta, luo sen sisältävän paketin ja lähettää sen kanavalle.

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 mahdollisten virheiden kanssa

Seuraava askel on olettaa, että kaikki lähetetyt paketit vastaanotetaan siinä järjestyksessä, jossa ne lähetettiin, mutta niissä olevat bitit voivat vioittua, 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 samanlaiset mekanismit lähetyksen toistamiseen useita kertoja, kutsutaan ARQ (Automatic Repeat ReQuest) -protokolliksi.
Lisäksi kannattaa huomioida mahdollisia virheitä kuiteissa, kun vastaanottava osapuoli ei saa mitään tietoa viimeisen paketin siirron tuloksista.
Ratkaisu tähän ongelmaan, jota käytetään myös TCP:ssä, on lisätä datapakettiin uusi kenttä, joka sisältää paketin järjestysnumeron.

Luotettavan tiedonsiirron perusteet

Luotettava tiedonsiirto epäluotettavalla kanavalla, joka on alttiina pakettien vääristymille ja häviämiselle

Vääristymisen ohella verkossa on valitettavasti myös pakettihäviöitä.
Ja tämän ongelman ratkaisemiseksi tarvitaan mekanismeja:

  • pakettien katoamisen tosiasian määrittäminen;
  • kadonneiden pakettien uudelleentoimittaminen vastaanottavalle osapuolelle.

Lisäksi paketin katoamisen lisäksi on huolehdittava kuitin katoamisen mahdollisuudesta tai jos mitään ei katoa, sen toimittamisesta huomattavalla viiveellä. Kaikissa tapauksissa tehdään sama asia: paketti lähetetään uudelleen. Ajan ohjaamiseen tämä mekanismi käyttää lähtölaskenta-ajastinta, jonka avulla voit määrittää odotusajan päättymisen. Paketissa siis netto TCPKeepAlive on oletusarvoisesti asetettu 15 sekuntiin:

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

Lähettävän puolen tulee käynnistää ajastin joka kerta kun paketti lähetetään (sekä ensimmäisellä että toisella kerralla), käsitellä ajastimen aiheuttamat keskeytykset ja pysäyttää se.

Olemme siis tutustuneet luotettavien tiedonsiirtoprotokollien avainkäsitteisiin:

  • tarkistussummat;
  • pakettien järjestysnumerot;
  • ajastimet;
  • positiiviset ja negatiiviset tulot.

Mutta ei siinä vielä kaikki!

Luotettava tiedonsiirtoprotokolla liukuhihnalla

Jo harkitsemassamme vaihtoehdossa luotettava toimitusprotokolla on erittäin tehoton. Se alkaa "hidastaa" viestintäkanavan tarjoamaa lähetystä RTT:n kasvaessa. Sen tehokkuuden lisäämiseksi ja viestintäkanavakapasiteetin hyödyntämiseksi paremmin käytetään liukuhihnaa.

Luotettavan tiedonsiirron perusteet

Putkiston käyttö johtaa:

  • järjestysnumeroiden alueen lisääminen, koska kaikki lähetetyt paketit (paitsi uudelleenlähetykset) on tunnistettava yksilöllisesti;
  • tarve lisätä puskureita lähetys- ja vastaanottopuolella.

Järjestysnumeroalueen ja puskurin kokovaatimukset riippuvat toimista, joita protokolla suorittaa vastauksena pakettien vioittumiseen, katoamiseen ja viivästymiseen. Liukuputkessa on kaksi tapaa korjata virheitä:

  • palauta N pakettia takaisin;
  • valikoiva toisto.

Paluu N pakettia - liukuva ikkuna protokolla

Luotettavan tiedonsiirron perusteet

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

  • soittaa korkeamman tason protokollalla. Kun tiedonlähetystoimintoa kutsutaan "ylhäältä", lähettäjäpuoli tarkistaa ensin ikkunan täyttöasteen (eli N lähetettyä viestiä odottamassa kuittausta). Jos ikkuna on tyhjä, uusi paketti luodaan ja lähetetään ja muuttujien arvot päivitetään. Muussa tapauksessa lähettävä puoli palauttaa tiedot ylempään kerrokseen, ja tämä on implisiittinen osoitus siitä, että ikkuna on täynnä. Yleensä ylempi kerros yrittää lähettää tiedot uudelleen jonkin ajan kuluttua. Oikeassa sovelluksessa lähettäjä todennäköisesti joko puskuroisi tiedot (sen sijaan että lähettäisi sen välittömästi) tai hänellä olisi synkronointimekanismi (kuten semafori tai lippu), jonka avulla ylempi kerros voisi kutsua lähetystoimintoa vain, kun ikkuna on tyhjä. .
  • vahvistuksen vastaanottaminen. Protokollassa paketille, jonka järjestysnumero on N, annetaan yleinen kuittaus, joka osoittaa, että kaikki paketit, joiden järjestysnumero on N:tä edeltävä, vastaanotettiin onnistuneesti.
  • odotusaika on umpeutunut. Protokolla käyttää ajastinta määrittääkseen pakettien ja kuittien katoamisen ja viivästymisen tosiasiat. Jos aikakatkaisuväli umpeutuu, lähettävä puoli lähettää uudelleen kaikki lähetetyt kuittamattomat paketit.

Valikoiva toisto

Kun ikkunan koko ja suoritusteho-etenemisviivetulo ovat suuria, suuri määrä paketteja voi olla valmisteilla. Tällaisessa tapauksessa yksittäinen pakettivirhe voi aiheuttaa suuren määrän paketteja uudelleenlähetyksen, joista suurinta osaa ei tarvita.

Esimerkki

Toppi teoreettinen käytännöt kerätään käytännön toteutuksessa TCP. Ja jos joku tietää kuinka parhaiten - 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 luotettavan tiedonsiirron ja käytön varmistamiseksi

Механизм
Hakemus, kommentti

Tarkista summa
Käytetään bittivirheiden havaitsemiseen lähetetyssä paketissa

ajastin
Laskee aikakatkaisuväliä ja ilmoittaa, milloin se on kulunut umpeen. Jälkimmäinen tarkoittaa, että suurella todennäköisyydellä paketti tai sen vastaanotto menetetään lähetyksen aikana. Jos paketti toimitetaan viiveellä, mutta ei katoa (aikakatkaisuvälin ennenaikainen umpeutuminen) tai kuitti katoaa, uudelleenlähetys johtaa paketin kaksoiskappaleeseen vastaanottavalla puolella

Sarjanumero
Käytetään lähettäjältä vastaanottajalle lähetettyjen datapakettien peräkkäiseen numerointiin. Vastaanotettujen pakettien järjestysnumeroiden aukot sallivat vastaanottimen havaita pakettien katoamisen. Samat pakettien järjestysnumerot tarkoittavat, että paketit ovat toistensa kaksoiskappaleita

vahvistus
Vastaanottavan pään luoma ja osoittava lähetyspäälle, että vastaava paketti tai pakettiryhmä on vastaanotettu onnistuneesti. Tyypillisesti kuittaus sisältää onnistuneesti vastaanotettujen pakettien järjestysnumerot. Protokollasta riippuen erotetaan yksilölliset ja ryhmävahvistukset

Negatiivinen vahvistus
Vastaanottaja käyttää tätä ilmoittamaan lähettäjälle, että paketti on vastaanotettu väärin. Negatiivinen kuittaus sisältää yleensä sen paketin järjestysnumeron, jota ei vastaanotettu oikein

Ikkuna, kuljetin
Rajoita pakettien lähettämiseen käytettävien järjestysnumeroiden määrää. Monilähetys ja kättely voivat lisätä merkittävästi protokollan suorituskykyä vahvistusten odottamiseen verrattuna. Kuten näemme, ikkunan koko voidaan laskea vastaanottavan pään vastaanotto- ja puskurointiominaisuuksien sekä verkon kuormitustason perusteella.

Lisää esimerkkejä Go-sovelluksesta verkkokäyttöön

В arkistot.

Lähde: will.com

Lisää kommentti