Tiems, kurie
Trumpai
Straipsnyje aptariami patikimo duomenų perdavimo pagrindai, pateikiami pavyzdžiai
Transporto sluoksnio protokolas
Užtikrina loginį ryšį tarp taikomųjų programų procesų, veikiančių skirtinguose pagrindiniuose kompiuteriuose. Programos požiūriu loginis ryšys atrodo kaip kanalas, tiesiogiai jungiantis procesus.
Tai atliekama padalinant (jei reikia) programos sluoksnio pranešimus į fragmentus ir prie kiekvieno iš jų pridedant transporto sluoksnio antraštę.
Tada transportavimo sluoksnis perduoda segmentą į siuntėjo tinklo sluoksnį, kur segmentas įterpiamas į tinklo sluoksnio paketą (duomenųgramą) ir išsiunčiamas. Priėmimo gale tinklo sluoksnis iš datagramos ištraukia transporto sluoksnio segmentą ir perduoda jį transportavimo sluoksniui. Tada transporto sluoksnis apdoroja gautą segmentą, kad jo duomenys taptų prieinami priimančiajai programai.
Patikimo duomenų perdavimo principai
Patikimas duomenų perdavimas visiškai saugiu kanalu
Paprasčiausias atvejis. Siunčianti pusė tiesiog gauna duomenis iš viršutinio sluoksnio, sukuria paketą su jais ir siunčia į kanalą.
Serveris
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 + надежный канал
}
}
Klientas
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)
}
}
Patikimas duomenų perdavimas kanalu su galimomis klaidomis
Kitas žingsnis – daryti prielaidą, kad visi perduoti paketai gaunami tokia tvarka, kokia buvo išsiųsti, tačiau juose esantys bitai gali būti sugadinti dėl to, kad kanalas kartais perduoda duomenis su iškraipymais.
Šiuo atveju naudojami šie mechanizmai:
- klaidų aptikimas;
- Atsiliepimas;
- retransliacija.
Patikimi duomenų perdavimo protokolai, turintys panašius perdavimo kelis kartus kartojimo mechanizmus, vadinami automatinio kartojimo užklausos (ARQ) protokolais.
Be to, verta pagalvoti apie klaidų galimybę kvituose, kai gaunančioji šalis negaus jokios informacijos apie paskutinio paketo perdavimo rezultatus.
Šios problemos sprendimas, taip pat naudojamas TCP, yra pridėti naują lauką prie duomenų paketo, kuriame yra paketo eilės numeris.
Patikimas duomenų perdavimas nepatikimu kanalu dėl paketų iškraipymo ir praradimo
Kartu su iškraipymais, deja, tinkle prarandami ir paketai.
Ir norint išspręsti šią problemą, reikalingi mechanizmai:
- paketų praradimo fakto nustatymas;
- pamestų paketų pakartotinis pristatymas gaunančiajai šaliai.
Be to, be pakuotės praradimo, būtina numatyti galimybę prarasti kvitą arba, jei nieko neprarandama, pristatyti jį gerokai vėluojant. Visais atvejais daroma ta pati: paketas persiunčiamas. Laikui valdyti šis mechanizmas naudoja atgalinės atskaitos laikmatį, leidžiantį nustatyti laukimo intervalo pabaigą. Taigi pakuotėje
// defaultTCPKeepAlive is a default constant value for TCPKeepAlive times
// See golang.org/issue/31510
const (
defaultTCPKeepAlive = 15 * time.Second
)
Siunčianti pusė turi paleisti laikmatį kiekvieną kartą, kai siunčiamas paketas (ir pirmą, ir antrą kartą), tvarkyti laikmačio trikdžius ir jį sustabdyti.
Taigi, mes susipažinome su pagrindinėmis patikimų duomenų perdavimo protokolų sąvokomis:
- kontrolinės sumos;
- pakuočių eilės numeriai;
- laikmačiai;
- teigiami ir neigiami kvitai.
Bet tai dar ne viskas!
Patikimas duomenų perdavimo protokolas su vamzdynais
Mūsų jau svarstytame variante patikimas pristatymo protokolas yra labai neefektyvus. Didėjant RTT, jis pradeda „lėtinti“ ryšio kanalo teikiamą perdavimą. Siekiant padidinti jo efektyvumą ir geriau išnaudoti ryšio kanalo pajėgumus, naudojamas vamzdynas.
Vamzdynų naudojimas lemia:
- eilės numerių diapazono didinimas, nes visi siunčiami paketai (išskyrus pakartotinius siuntimus) turi būti unikaliai identifikuoti;
- poreikis padidinti buferius siuntimo ir priėmimo pusėse.
Eilės numerių diapazonas ir buferio dydžio reikalavimai priklauso nuo veiksmų, kuriuos protokolas atlieka reaguodamas į paketų sugadinimą, praradimą ir vėlavimą. Vamzdynų klojimo atveju yra du klaidų ištaisymo būdai:
- grąžinti N paketų atgal;
- selektyvus kartojimas.
Grįžtant atgal N paketų – stumdomo lango protokolas
Siuntėjas turi palaikyti trijų tipų įvykius:
- skambinti aukštesnio lygio protokolu. Kai duomenų siuntimo funkcija vadinama „iš viršaus“, siunčiančioji pusė pirmiausia patikrina lango užpildymo laipsnį (tai yra, ar yra N išsiųstų pranešimų, laukiančių gavimo kvitų). Jei langas tuščias, generuojamas ir perduodamas naujas paketas, atnaujinamos kintamųjų reikšmės. Kitu atveju siunčianti pusė grąžina duomenis į viršutinį sluoksnį, ir tai yra netiesioginis požymis, kad langas pilnas. Paprastai viršutinis sluoksnis po kurio laiko vėl bandys perduoti duomenis. Tikroje programoje siuntėjas greičiausiai arba buferuotų duomenis (o ne iš karto išsiųstų) arba turės sinchronizacijos mechanizmą (pvz., semaforą ar vėliavėlę), kuris leistų viršutiniam sluoksniui iškviesti siuntimo funkciją tik tada, kai langas tuščias. .
- gauti patvirtinimą. Protokole paketui su eilės numeriu N išduodamas bendras patvirtinimas, nurodantis, kad visi paketai, kurių eilės numeriai yra prieš N, buvo sėkmingai gauti.
- laukimo intervalas baigėsi. Paketų ir kvitų praradimo ir vėlavimo faktams nustatyti protokole naudojamas laikmatis. Jei laikas pasibaigia, siunčiančioji pusė iš naujo siunčia visus išsiųstus nepatvirtintus paketus.
Atrankinis kartojimas
Kai lango dydis ir pralaidumo plitimo delsos produktas yra dideli, gali būti ruošiamas didelis paketų skaičius. Tokiu atveju dėl vieno paketo klaidos gali būti pakartotinai perduota daug paketų, kurių dauguma nebuvo reikalingi.
Pavyzdys
Viršus
Serveris
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"))
}
}
Klientas
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)
}
}
Produkcija
Mechanizmai, užtikrinantys patikimą duomenų perdavimą ir naudojimą
Mechanizmas
Paraiška, komentaras
Patikrinkite sumą
Naudojamas perduodamo paketo bitų klaidoms aptikti
Laikmatis
Skaičiuoja skirtojo laiko intervalą ir nurodo, kada jis pasibaigė. Pastarasis reiškia, kad siuntimo metu labai tikėtina, kad paketas arba jo gavimas prarandamas. Jei paketas pristatomas pavėluotai, bet neprarandamas (priešlaikinis laiko intervalo pasibaigimas) arba pametamas kvitas, pakartotinis siuntimas veda prie dublikato paketo gavimo pusėje.
Serijos numeris
Naudojamas nuosekliai numeruoti duomenų paketus, perduodamus iš siuntėjo gavėjui. Gautų paketų eilės numerių spragos leidžia imtuvui aptikti paketų praradimą. Tie patys paketų eilės numeriai reiškia, kad paketai yra vienas kito dublikatai
Patvirtinimas
Sugeneruoja priėmimo galas ir nurodo siuntimo galui, kad atitinkamas paketas arba paketų grupė buvo sėkmingai gauti. Paprastai patvirtinime yra sėkmingai gautų paketų eilės numeriai. Priklausomai nuo protokolo, skiriami individualūs ir grupiniai patvirtinimai
Neigiamas patvirtinimas
Naudoja gavėjas informuodamas siuntėją, kad paketas gautas neteisingai. Neigiamas patvirtinimas paprastai apima neteisingai gauto paketo eilės numerį
Langas, konvejeris
Apribokite eilės numerių diapazoną, kuris gali būti naudojamas paketams perduoti. Daugialypė transliacija ir rankos paspaudimas gali žymiai padidinti protokolo pralaidumą, palyginti su laukimu, kol laukiama patvirtinimų. Kaip matysime, lango dydis gali būti apskaičiuojamas pagal priėmimo ir buferio galimybes, taip pat tinklo apkrovos lygį.
Daugiau „Go“ naudojimo tinkle pavyzdžių
В
Šaltinis: www.habr.com