
Neile, kes Pühendunud võrkude ja protokollide mõistmisele.
Lühidalt
Artiklis käsitletakse usaldusväärse andmeedastuse põhitõdesid ja tuuakse näiteid , sealhulgas UDP ja TCP. Põhineb , , ja raamatut "Arvutivõrgud: ülalt-alla lähenemine", muidu arutavad kõik ainult Tannenbaumi ja Oliferovi üle.
Transpordikihi protokoll
Pakub loogilist ühendust erinevatel hostidel töötavate rakendusprotsesside vahel. Rakenduste vaatenurgast paistab loogiline ühendus protsesse otse ühendava kanalina.

lõppsüsteemide, aga mitte võrguruuterite poolt toetatud (välja arvatud - Saatja poolel teisendab transpordikiht edastavalt rakendusprotsessilt vastuvõetud rakenduskihi andmed transpordikihi pakettideks, mida nimetatakse segmentideks.

Seda tehakse rakenduskihi sõnumite (vajadusel) fragmentideks jagamise ja igale neist transpordikihi päise lisamise teel.

Seejärel edastab transpordikiht segmendi saatja võrgukihile, kus see kapseldatakse võrgukihi paketti (datagrammi) ja saadetakse. Vastuvõtvas otsas eraldab võrgukiht transpordikihi segmendi datagrammist ja edastab selle transpordikihile. Seejärel töötleb transpordikiht vastuvõetud segmenti nii, et selle andmed oleksid vastuvõtvale rakendusele kättesaadavad.

Turvalise andmeedastuse põhimõtted
Usaldusväärne andmeedastus täiesti turvalise kanali kaudu
Lihtsaim juhtum: saatja pool lihtsalt võtab ülemiselt kihilt andmeid vastu, loob neid sisaldava paketi ja saadab need kanali kaudu edasi.
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 + надежный канал
}
}Klient
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)
}
}Usaldusväärne andmeedastus üle kanali, kus on võimalikud vead
Järgmine samm on eeldada, et kõik edastatud paketid võetakse vastu nende saatmise järjekorras, kuid nendes olevad bitid võivad olla rikutud, kuna kanal edastab mõnikord andmeid moonutustega.

Sellisel juhul rakendatakse järgmisi mehhanisme:
- vigade tuvastamine;
- tagasiside;
- taasedastamine.
Usaldusväärseid andmeedastusprotokolle, millel on sellised mehhanismid mitmekordseks edastuskorduseks, nimetatakse automaatseks kordusnõustamiseks (ARQ) protokollideks.
Lisaks tasub kaaluda kviitungite vigade võimalikkust, kui vastuvõttev pool ei saa viimase paketiedastuse tulemuste kohta mingit teavet.
Selle probleemi lahendus, mida kasutatakse muuhulgas TCP-s, on andmepaketti uue välja lisamine, mis sisaldab paketi järjekorranumbrit.

Usaldusväärne andmeedastus ebausaldusväärse kanali kaudu, mis võimaldab pakettide moonutamist ja kadumist
Lisaks moonutustele esineb võrgus kahjuks ka pakettide kadu.
Ja selle probleemi lahendamiseks on vaja mehhanisme:
- pakettide kadumise fakti kindlakstegemine;
- kadunud pakettide uuesti kättetoimetamine vastuvõtjale.
Lisaks pakettide kadumisele on vaja arvestada ka kviitungi kadumise võimalusega või kui midagi ei kao, siis selle kohaletoimetamisega olulise viivitusega. Kõigil juhtudel toimub sama toiming: pakett saadetakse uuesti. Ajastuse juhtimiseks kasutab see mehhanism taimerit, mis võimaldab määrata ooteintervalli lõppu. Seega paketis Vaikimisi on TCPKeepAlive seatud 15 sekundile:
// defaultTCPKeepAlive is a default constant value for TCPKeepAlive times
// See golang.org/issue/31510
const (
defaultTCPKeepAlive = 15 * time.Second
)Saatja peab taimeri käivitama iga kord, kui pakett edastatakse (nii esimesel kui ka teisel korral), käsitlema taimeri katkestusi ja selle peatama.
Seega oleme tutvunud usaldusväärsete andmeedastusprotokollide põhimõistetega:
- kontrollsummad;
- pakendite seerianumbrid;
- taimerid;
- positiivsed ja negatiivsed laekumised.
Aga see pole veel kõik!
Usaldusväärne andmeedastusprotokoll torujuhtmega
Eelnevalt käsitletud rakenduses on usaldusväärne edastusprotokoll väga ebaefektiivne. See hakkab RTT suurenedes sidekanali pakutavat edastust "aeglustama". Selle efektiivsuse parandamiseks ja sidekanali ribalaiuse paremaks ärakasutamiseks kasutatakse torujuhtmetehnoloogiat.

Torujuhtmete kasutamine annab tulemuseks:
- järjekorranumbrite vahemiku suurendamine, kuna kõik saadetud paketid (välja arvatud korduseksdastamised) peavad olema üheselt tuvastatavad;
- vajadus suurendada saatja ja vastuvõtja puhvreid.
Järjekorranumbrite vahemik ja puhvri suuruse nõuded sõltuvad protokolli reaktsioonist pakettide rikkumisele, kadumisele ja viivitusele. Torujuhtme puhul on kaks veaparandusmeetodit:
- tagasta N paketti tagasi;
- valikuline kordamine.
Tagasiulatuvad N paketid - libiseva akna protokoll

Saatja peab toetama kolme tüüpi sündmusi:
- Kõrgema taseme protokolli kutse. Kui saatmisfunktsiooni kutsutakse ülaltpoolt, kontrollib saatja kõigepealt, kas aken on täis (st kas on N saadetud sõnumit, mis ootavad kinnitamist). Kui aken pole täis, moodustatakse ja edastatakse uus pakett ning muutujate väärtusi uuendatakse. Vastasel juhul tagastab saatja andmed ülemisele kihile, mis on kaudne märk sellest, et aken on täis. Tavaliselt proovib ülemine kiht andmeid mõne aja pärast uuesti edastada. Reaalses rakenduses saatja tõenäoliselt kas puhverdab andmeid (selle asemel, et neid kohe saata) või kasutab sünkroniseerimismehhanismi (nt semafor või lipp), mis võimaldaks ülemisel kihil saatmisfunktsiooni kutsuda ainult siis, kui aken pole täis.
- Kviitungi vastuvõtmine. Protokollis väljastatakse järjekorranumbriga N paketi kohta üldine kviitung, mis näitab, et kõik N-ile eelneva järjekorranumbriga paketid on edukalt vastu võetud.
- Aja möödumine. Protokoll kasutab taimerit pakettide ja kinnituste kadumise ning viivituste tuvastamiseks. Kui ajalõpp möödub, saadab saatja kõik kinnitamata paketid uuesti.
Valikuline kordamine
Kui akna suurus ja läbilaskevõime-viivituse korrutis on suured, võib torujuhtmes olla suur hulk pakette. Sellisel juhul võib ühe paketi viga põhjustada suure hulga pakettide uuesti saatmise, millest enamikku polnud vaja.
Näide
Top praktikas kogutakse praktikas rakendamise tavasid Ja kui keegi teab paremat viisi - .
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"))
}
}Klient
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)
}
}Väljund
Mehhanismid, mis tagavad usaldusväärse andmeedastuse ja -kasutuse
Mehhanism
Rakendus, kommentaar
Kontrolli summa
Kasutatakse edastatud paketis olevate bitivigade tuvastamiseks.
taimer
Ajalõpuintervalli loendur ja selle aegumise näit. Viimane näitab suure tõenäosusega, et pakett või selle kinnitus läks edastamise ajal kaduma. Kui pakett toimetatakse kohale hilja, kuid ei kao (ajalõpuintervalli enneaegne aegumine) või kui kinnitus läheb kaduma, põhjustab uuesti saatmine vastuvõtja poolel duplikaatpaketi.
Seerianumber
Kasutatakse saatjalt vastuvõtjale edastatavate andmepakettide järjekorranumbrite määramiseks. Vastuvõetud pakettide järjekorranumbrite lüngad võimaldavad vastuvõtjal tuvastada pakettide kadu. Identsed pakettide järjekorranumbrid näitavad duplikaatpakette.
Kinnitus
Vastuvõtva poole poolt genereeritud ja edastavale poolele näidates, et vastav pakett või pakettide rühm on edukalt vastu võetud. Kinnitused sisaldavad tavaliselt edukalt vastuvõetud pakettide järjekorranumbreid. Sõltuvalt protokollist eristatakse individuaalseid ja grupikinnitusi.
Negatiivne kinnitus
Saaja kasutab seda saatja teavitamiseks, et paketti ei saadud õigesti vastu. Negatiivne kinnitus sisaldab tavaliselt valesti vastu võetud paketi järjekorranumbrit.
Aken, torujuhe
Need piiravad pakettide edastamiseks kasutatavate järjenumbrite vahemikku. Multisaadete edastamine ja käepigistus suurendavad protokolli läbilaskevõimet märkimisväärselt võrreldes kinnituste ootamisega. Nagu näeme, saab akna suurust arvutada vastuvõtva poole vastuvõtu- ja puhverdusvõimaluste ning võrgukoormuse põhjal.
Muud näited Go kasutamisest võrgustamiseks
В .
Allikas: www.habr.com
