
Tým, ktorí sa chcú zorientovať v sieťach a protokoloch, je zasvätené.
Stručne
V článku sú preskúmané základy spoľahlivého prenosu dát, realizujú sa príklady na , vrátane UDP a TCP. Podľa , , a knihy "Počítačové siete. Zostupný prístup", pretože všetci diskutujú len o Tanenbaumovi a Oliferovi.
Transportný protokol
Zabezpečuje logické spojenie medzi aplikačnými procesmi, ktoré bežia na rôznych hostiteľoch. Z pohľadu aplikácií vyzerá logické spojenie ako kanál, ktorý priamo spája procesy.

sú podporované koncovými systémami, ale nie sieťovými smerovačmi (okrem - ). Na strane odosielateľa transportná úroveň transformuje dáta aplikačnej úrovne, ktoré prijíma od odosielaného aplikačného procesu, na pakety transportnej úrovne nazývané segmenty.

To sa dosahuje rozdelením (ak je to potrebné) správ aplikačnej úrovne na fragmenty a pridaním hlavičky transportnej úrovne k každému z nich.

Následne transportná úroveň odosiela segment sieťovej úrovni odosielateľa, kde je segment zabalený do paketu sieťovej úrovne (datagramu) a odoslaný. Na prijímajúcej strane sieťová úroveň vyberá segment transportnej úrovne z datagramu a posúva ho smerom nahor na transportnú úroveň. Transportná úroveň potom spracováva prijatý segment tak, aby jeho údaje boli dostupné prijímacej aplikácii.

Princípy spoľahlivého prenosu dát
Spoľahlivý prenos dát cez úplne spoľahlivý kanál
Jednoduchý prípad. Odosielajúca strana jednoducho prijíma údaje z horného levelu, vytvára balík, ktorý ich obsahuje, a odosiela ho do kanála.
Server
package main
import (
"log"
"net"
)
func main() {
// IP adresa servera a port
serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
if err != nil {
log.Fatal(err)
}
// vytvorenie soketu s portom
serverConn, err := net.ListenUDP("udp", serverAddr)
if err != nil {
log.Fatal(err)
}
// oneskorené zatvorenie pripojenia
defer serverConn.Close()
// vytvorenie buffera pre dáta
buf := make([]byte, 1024)
// čakáme na pripojenie
for {
// čítame požiadavku
n, addr, err := serverConn.ReadFromUDP(buf)
// odosielame dáta na vyššiu úroveň: v našom prípade stdout
println(string(buf[0:n]), " form ", addr.IP.String())
if err != nil {
log.Fatal(err)
}
// odpovedať nie je potrebné, keďže ide o UDP + spoľahlivý kanál
}
}Klienta
package main
import (
"fmt"
"log"
"net"
"time"
)
func main() {
// IP adresa servera a port
serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
if err != nil {
log.Fatal(err)
}
// miestna IP adresa a port
localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
// nadviazanie pripojenia
conn, err := net.DialUDP("udp", localAddr, serverAddr)
if err != nil {
log.Fatal(err)
}
// oneskorené zatvorenie pripojenia
defer conn.Close()
for {
// získavanie dát z vyššej úrovne
fmt.Print("Zadajte veta > ")
var msg string
_, err := fmt.Scanf("%s", &msg)
if err != nil {
log.Fatal(err)
}
// odosiela sa prúd bajtov, nie reťazec
buf := []byte(msg)
// zápis (odoslanie) do pripojenia
_, err = conn.Write(buf)
if err != nil {
log.Fatal(err)
}
// 1 sekunda počkať
time.Sleep(time.Second * 1)
}
}Spoľahlivý prenos dát cez kanál s možnými chybami
Ďalším krokom je predpoklad, že všetky odoslané pakety sú prijaté v tom poradí, v ktorom boli odoslané, avšak bity v nich môžu byť poškodené, pretože kanál občas prenáša dáta s deformáciou.

V takomto prípade sú použité mechanizmy:
- zistenia chýb;
- spätná väzba;
- opakovaného odoslania.
Protokoly spoľahlivého prenosu dát, ktoré majú podobné mechanizmy opakovaného prenosu, sa nazývajú protokoly s automatickým žiadosťou o opakovanie prenosu (Automatic Repeat reQuest, ARQ).
Navyše, je potrebné zohľadniť možnosť chýb aj v potvrdeniach, keď prijímajúca strana nedostane žiadne informácie o výsledkoch prenosu posledného paketu.
Riešením tejto úlohy, ktoré sa používa aj v TCP, je pridanie nového poľa do paketu, ktoré obsahuje poradové číslo paketu.

Spoľahlivé prenos dát cez nespoľahlivý kanál, ktorý dovoľuje skreslenie a stratu paketov
Žiaľ, popri skresleniach v sieti je prítomná aj strata paketov.
Na vyriešenie tejto úlohy sú potrebné mechanizmy:
- určenia straty paketov;
- opätovného zaslania stratenej pakety prijímajúcej strane.
Okrem straty paketu je dôležité zabezpečiť aj možnosť straty potvrdenia alebo, ak nič nebolo stratené, jej doručenie s významným oneskorením. Vo všetkých prípadoch sa vykonáva to isté: opätovný prenos paketu. Na kontrolu času v tomto mechanizme sa používa odpočítavací časovač, ktorý umožňuje určiť koniec čakacej doby. Tak v pakete parametre TCPKeepAlive sú nastavené na 15 sekúnd ako predvolené:
// defaultTCPKeepAlive is a default constant value for TCPKeepAlive times
// See golang.org/issue/31510
const (
defaultTCPKeepAlive = 15 * time.Second
)Odosielajúca strana musí spustiť časovač zakaždým pri odoslaní paketu (ako pri prvom, tak aj pri opätovnom), spracovávať prerušenia z časovača a zastaviť ho.
Takže sme sa oboznámili s kľúčovými pojmami protokolov spoľahlivého prenosu dát:
- kontrolnými súčtami;
- poradovými číslami paketov;
- časovačmi;
- kladnými a zápornými potvrdeniami.
Ale to nie je všetko!
Protokol spoľahlivého prenosu dát s kanbanizáciou
V prípade, ktorý sme už preskúmali, je protokol spoľahlivej dodávky veľmi neefektívny. Pri zvyšovaní RTT začína "zadržiavať" prenos zabezpečený komunikačným kanálom. Na zlepšenie jeho efektivity a lepšie využitie šírky pásma komunikačného kanála sa používa kanbanizácia.

Použitie kanbanizácie vedie k:
- zvýšeniu rozsahu poradových čísel, pretože všetky odoslané pakety (okrem opätovných prenosov) musia byť jednoznačne identifikovateľné;
- potrebe zvýšiť buffery na odosielajúcej a prijímajúcej strane.
Rozsah poradových čísel a požiadavky na veľkosť bufferov závisia od činov, ktoré protokol podniká v reakcii na skreslenie, stratu a oneskorenie paketu. V prípade kanbanizácie existujú dva spôsoby opravy chýb:
- návrat o N paketov späť;
- výberové opakovanie.
Návrat o N paketov späť — protokol kĺzavého okna

Odosielateľ musí sledovať tri typy udalostí:
- volanie protokolom vyššej úrovne. Keď je vyvolaná funkcia na odosielanie údajov, odosielajúca strana najskôr skontroluje úroveň zaplnenia okna (to znamená počet odoslaných správ, ktoré čakajú na potvrdenie). Ak je okno nezaplné, nová správa sa vytvorí a odošle, a hodnoty premenných sa aktualizujú. Inak odosielajúca strana vracia údaje vyššej úrovni, čo je implicitná indikácia, že okno je zaplnené. Zvyčajne sa vyššia úroveň pokúša o opätovné odoslanie údajov po určitom čase. V reálnej aplikácii by odosielateľ pravdepodobne buď vyrovnával údaje (namiesto okamžitého odosielania), alebo mal mechanizmus synchronizácie (napríklad semafor alebo flag), ktorý by umožnil vyššej úrovni vyvolať funkciu na odosielanie údajov len pri nezaplnom okne.
- potvrdenie prijatia. V protokole pre paket s poradovým číslom N sa vydáva celkové potvrdenie, ktoré indikuje, že všetky pakety s poradovými číslami predchádzajúcimi N boli úspešne prijaté.
- vypršanie časového limitu. Na určenie strát a oneskorení paketov a potvrdení protokol používa časovač. Ak časový limit vyprší, odosielajúca strana znovu odošle všetky odoslané potvrdzujúce pakety.
vyberané opakovanie
Keď je veľkosť okna a súčin priepustnosti a oneskorenia veľký, v potrubí môže byť veľké množstvo paketov. V takom prípade chyba jedného paketu môže spôsobiť opakované prenášanie veľkého množstva paketov, z ktorých väčšina nebola potrebná.
Príklad
Najlepšie praktiky sú zozbierané v praktickej realizácii . A ak niekto vie, ako lepšie — .
Server
package main
import (
"bufio"
"fmt"
"log"
"net"
"strings"
)
func main() {
// vytvoríme soket s portom
ln, err := net.Listen("tcp", ":8081")
if err != nil {
log.Fatalln(err)
}
// čakáme na volanie
conn, _ := ln.Accept()
for {
// čítanie údajov
msg, err := bufio.NewReader(conn).ReadString('n')
if err != nil {
log.Fatalln(err)
}
// výstup správy do stdout
fmt.Print("Správa prijatá:", string(msg))
// premena reťazca na veľké písmená
newMsg := strings.ToUpper(msg)
// odosielanie údajov
conn.Write([]byte(newMsg + "n"))
}
}Klienta
package main
import (
"bufio"
"fmt"
"log"
"net"
"os"
)
func main() {
// nastavenie pripojenia
conn, err := net.Dial("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatalln(err)
}
for {
// čítanie dát z stdin
reader := bufio.NewReader(os.Stdin)
fmt.Print("Text na odoslanie: ")
// po riadkoch
text, err := reader.ReadString('n')
if err != nil {
log.Fatalln(err)
}
// odoslanie
fmt.Fprintf(conn, text+"n")
// prijatie
msg, err := bufio.NewReader(conn).ReadString('n')
if err != nil {
log.Fatalln(err)
}
// výstup prijatej odpovede
fmt.Print("Správa zo servera: " + msg)
}
}Záver
Mechanizmy zabezpečujúce spoľahlivý prenos dát a ich použitie
Mechanizmus
Použitie, komentár
Kontrolný súčet
Používa sa na odhaľovanie bitových chýb v prenášanom pakete
Časovač
Odpočet časového intervalu a označenie jeho uplynutia. Posledné znamená, že s vysokou pravdepodobnosťou je paket alebo jeho potvrdenie stratené pri prenose. V prípade, že paket je doručený so zdržaním, ale nie je stratený (predčasné uplynutie časového intervalu), alebo dochádza k strate potvrdenia, opakované odoslanie vedie k duplicite paketu na prijímajúcej strane
Poradové číslo
Používa sa na poradovú numeráciu paketov dát prenášaných od odosielateľa k prijímateľovi. Medzery v poradových číslach prijatých paketov umožňujú prijímateľovi odhaliť stratu paketu. Rovnako poradové čísla paketov znamenajú, že pakety sú duplikáty
Potvrdenie
Generuje sa prijímajúcou stranou a označuje odosielajúcej strane, že príslušný paket alebo skupina paketov bola úspešne prijatá. Zvyčajne potvrdenie obsahuje poradové čísla úspešne prijatých paketov. V závislosti od protokolu sa rozlišujú individuálne a skupinové potvrdenia
Negatívne potvrdenie
Používa sa prijímateľom na oznámenie odosielateľovi, že paket bol prijatý nesprávne. Negatívne potvrdenie obvykle zahŕňa poradové číslo paketu, ktorý nebol správne prijatý
Okno, kanálové spracovanie
Obmedzujú rozsah poradových čísel, ktoré môžu byť použité na prenos paketov. Skupinový prenos a handshake umožňujú výrazne zvýšiť priepustnosť protokolov v porovnaní s režimom čakajúceho potvrdenia. Ako uvidíme, veľkosť okna môže byť vypočítaná na základe prijímacích a bufferingových schopností prijímacej strany, ako aj úrovne zaťaženia siete.
Ďalšie príklady využitia Go pri práci so sieťou.
V .
Zdroj: habr.com
