
Atyre që meritojnë të kuptojnë rrjetet dhe protokollet.
Shkurt
Ky artikull shqyrton bazat e transmetimit të sigurt të të dhënave, duke realizuar shembuj në , duke përfshirë UDP dhe TCP. Në frymën e , , dhe librit "Rrjetet kompjuterike. Qasja nga lart poshtë", sepse të gjithë flasin vetëm për Tanenbaumin dhe Oliferët.
Protokolli i nivelit transportit
siguron një lidhje logjike midis proceseve aplikative që punojnë në hoste të ndryshme. Një lidhje logjike nga pikëpamja e aplikacioneve duket si një kanale, e cila lidh drejtpërdrejt proceset.

mbĂ«shteten nga sistemet pĂ«rfundimtare, por jo nga rrugĂ«tuesit e rrjetit (pĂ«rveç â ). NĂ« anĂ«n e dĂ«rguesit, niveli transportit konverton tĂ« dhĂ«nat e nivelit aplikativ, tĂ« cilat i merr nga procesi aplikativ dĂ«rgues, nĂ« paketa tĂ« nivelit transportit, tĂ« quajtura segmente.

Kjo bëhet duke ndarë (në rast nevoje) mesazhet e nivelit aplikativ në copëza dhe duke shtuar një kokë të nivelit transportit për secilën prej tyre.

Pastaj niveli transportit dërgon segmentin në nivelin e rrjetit të dërguesit, ku segmenti inkapsulohet në një paketë të nivelit të rrjetit (datagram) dhe dërgohet. Në anën e marrësit, niveli i rrjetit nxjerr segmentin e nivelit transportit nga datagrami dhe e dërgon lart në nivelin e transportit. Më pas niveli i transportit e përpunon segmentin e marrë në mënyrë që të dhënat e tij të jenë të disponueshme për aplikacionin marrës.

Parimet e transmetimit të besueshëm të të dhënave
Transmetimi i besueshëm i të dhënave përmes një kanali tërësisht të besueshëm
Rasti më i thjeshtë. Anëtari dërgues thjesht merr të dhënat nga niveli i sipërm, krijon një paketë që përmban ato dhe e dërgon në kanal.
Serveri
package main
import (
"log"
"net"
)
func main() {
// Adresa IP e serverit dhe porta
serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
if err != nil {
log.Fatal(err)
}
// krijojmë socket me portin
serverConn, err := net.ListenUDP("udp", serverAddr)
if err != nil {
log.Fatal(err)
}
// mbyllja e vonuar e lidhjes
defer serverConn.Close()
// krijojmë një tampon për të dhënat
buf := make([]byte, 1024)
// presim lidhjen
for {
// lexojmë kërkesën
n, addr, err := serverConn.ReadFromUDP(buf)
// dĂ«rgojmĂ« tĂ« dhĂ«nat nĂ« NIVELIN E SIPĂRM: nĂ« rastin tonĂ« stdout
println(string(buf[0:n]), " nga ", addr.IP.String())
if err != nil {
log.Fatal(err)
}
// nuk ka përgjigje, sepse është UDP + kanal i besueshëm
}
}Klienti
package main
import (
"fmt"
"log"
"net"
"time"
)
func main() {
// Adresa IP e serverit dhe porta
serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
if err != nil {
log.Fatal(err)
}
// adresa IP lokale dhe porta
localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
// vendosur lidhjen
conn, err := net.DialUDP("udp", localAddr, serverAddr)
if err != nil {
log.Fatal(err)
}
// mbyllja e vonuar e lidhjes
defer conn.Close()
for {
// marrja e tĂ« dhĂ«nave nga NIVELI I SIPĂRM
fmt.Print("Shkruani një fjali të thjeshtë > ")
var msg string
_, err := fmt.Scanf("%s", &msg)
if err != nil {
log.Fatal(err)
}
// dërgohet një rrjedhë bajtash, jo një varg
buf := []byte(msg)
// shkrimi (dërgimi) në lidhje
_, err = conn.Write(buf)
if err != nil {
log.Fatal(err)
}
// një sekondë
time.Sleep(time.Second * 1)
}
}Transmetimi i besueshëm i të dhënave në një kanal me mundësi gabimesh
Hapi tjetĂ«r â Ă«shtĂ« supozimi qĂ« tĂ« gjitha paketat e dĂ«rguara janĂ« marrĂ« nĂ« rendin nĂ« tĂ« cilin janĂ« dĂ«rguar, por bitĂ«t nĂ« to mund tĂ« jenĂ« dĂ«mtuar, pĂ«r shkak se kanali ndonjĂ«herĂ« transmeton tĂ« dhĂ«na me deformime.

Në këtë rast përdoren mekanizmat:
- zbullimi i gabimeve;
- feedback;
- riprodhimi.
Protokollat e transmetimit të besueshëm të të dhënave që kanë mekanizma të tillë të riprodhimit, quhen protokolle me kërkesë automatike për riprodhim (Automatic Repeat reQuest, ARQ).
Për më tepër, duhet parashikuar mundësia e gabimeve edhe në konfirmimet, kur pala marrëse nuk merr asnjë informacion mbi rezultatet e transmetimit të paketës së fundit.
Zgjidhja e këtij problemi, e përdorur gjithashtu në TCP, është të shtohet në paketën e të dhënave një fushë të re që përmban numrin rendor të paketës.

Transmetimi i besueshëm i të dhënave në një kanal të pasigurt, i cili lejon deformim dhe humbje paketash
Në të njëjtën kohë me deformimet, fatkeqësisht, në rrjet ka humbje paketash.
Dhe për zgjidhjen e këtij problemi kërkohen mekanizmat:
- përcaktimi i humbjes së paketave;
- riprodhimi i paketave të humbura për palën marrëse.
Përveç humbjes së paketave, është e nevojshme të parashikohet mundësia e humbjes së konfirmimeve ose, nëse asgjë nuk humbet, dërgimi i saj me vonesë të konsiderueshme. Në të gjitha rastet, çfarëdo që ndodh, përsëritet dërgimi i paketës. Për të kontrolluar kohën në këtë mekanizëm përdoret një temporizues, i cili lejon përcaktimin e përfundimit të intervalit të pritjes. Kështu në paketë parametri TCPKeepAlive është i vendosur në 15 sekonda si default:
// defaultTCPKeepAlive is a default constant value for TCPKeepAlive times
// See golang.org/issue/31510
const (
defaultTCPKeepAlive = 15 * time.Second
)Për dërguesin, është e nevojshme të aktivizojë temporizuesin çdo herë që dërgon një paketë (si për herën e parë ashtu edhe për përsëritjen), të përpunojë ndërprerjet nga temporizuesi dhe ta ndalë atë.
Kështu, jemi njohur me konceptet kyçe të protokolleve të transmetimit të besueshëm:
- kontrolli i shuma;
- numrat rendor të paketave;
- temporizuesat;
- konfirmimet pozitive dhe negative.
Por kjo nuk është e gjitha!
Protokolli i transmetimit të besueshëm me pipeline
Në variantin që kemi shqyrtuar, protokolli i dërgimit të besueshëm është shumë joefikas. Ai fillon të "ngadalësojë" transmetimin, i siguruar nga kanali i komunikimit, me rritjen e RTT. Për të përmirësuar efikasitetin e tij, për një shfrytëzim më të mirë të kapacitetit të kanalit të komunikimit përdoret konveyeri.

Përdorimi i konveyerizimit çon në:
- rritjen e gamës së numrave rendor, pasi të gjitha paketat e dërguara (përveç përsëritjeve) duhet të identifikohen qartësisht;
- nevojën për të rritur buffer-at në anën dërguese dhe ato marrëse.
Gama e numrave rendor dhe kërkesat për madhësitë e bufferave varen nga veprimet që ndërmerr protokolli në përgjigje të çrregullimeve, humbjeve dhe vonesave të paketave. Në rastin e konveyerizimit ekzistojnë dy metoda të korrigjimit të gabimeve:
- kthehu prapa me N paketa;
- ripërsëritje selektive.
Kthehu prapa me N paketa - protokolli i dritares lëvizëse

Dërguesi duhet të mbajë tre lloje ngjarjesh:
- thirrja nga protokolli i nivelit më të lartë. Kur funksioni i dërgimit të të dhënave thirret "më lart", ana dërguese kontrollon fillimisht shkallën e mbushjes së dritares (nëse ka N mesazhe të dërguara që presin për konfirmime). Nëse dritarja është e zbrazët, dërgohet një paketë e re dhe vlerat e variablave përditësohen. Në të kundërt, ana dërguese i kthen të dhënat nivelit të sipërm, dhe kjo është një tregues i heshtur që dritarja është e mbushur. Zakonisht, niveli i sipërm provon të ripërsërisë dërgimin e të dhënave pas një kohe. Në një aplikacion real, dërguesi me siguri do të kishte buferizuar të dhënat (përveç dërgimit të menjëhershëm) ose do të kishte një mekanizëm sinkronizimi (p.sh., semafor ose flamur) që do t'i lejonte nivelit të sipërm të thërriste funksionin e dërgimit të të dhënave vetëm kur dritarja është e zbrazët.
- marrja e konfirmimit. Në protokoll, për paketën me numrin rendor N jepet një konfirmim i përgjithshëm, që tregon se të gjitha paketat me numra rendor, që i paraprijnë N, janë pranuar me sukses.
- skadimi i intervalit të pritjes. Për të përcaktuar faktet e humbjeve dhe vonesave të paketave dhe konfirmimeve, protokolli përdor një temporizues. Nëse intervali i pritjes skadon, ana dërguese dërgon përsëri të gjitha paketat e dërguara që nuk kanë marrë konfirmime.
Ripërsëritja selektive
Kur madhësia e dritares dhe prodhimi i kapacitetit të bandës mbi vonesën e shpërndarjes janë të mëdha, në konveyer mund të ketë një numër të madh paketash. Në këtë rast, gabimi i një pakete të vetme mund të shkaktojë ripërsëritjen e një numri të madh paketash, shumica e të cilave nuk ishin të nevojshme.
Shembuj
Praktikat mĂ« tĂ« mira tĂ« mbledhura nĂ« njĂ« implementim praktik . Dhe nĂ«se dikush di njĂ« mĂ«nyrĂ« mĂ« tĂ« mirĂ« â .
Serveri
package main
import (
"bufio"
"fmt"
"log"
"net"
"strings"
)
func main() {
// krijo një socket me port
ln, err := net.Listen("tcp", ":8081")
if err != nil {
log.Fatalln(err)
}
// prit thirrjen
conn, _ := ln.Accept()
for {
// leximi i të dhënave
msg, err := bufio.NewReader(conn).ReadString('n')
if err != nil {
log.Fatalln(err)
}
// printimi i mesazhit në stdout
fmt.Print("Mesazhi i Marrë:", string(msg))
// shndërrimi i tekstit në shkronja të mëdha
newMsg := strings.ToUpper(msg)
// dërgimi i të dhënave
conn.Write([]byte(newMsg + "n"))
}
}Klienti
paketa kryesore
import (
"bufio"
"fmt"
"log"
"net"
"os"
)
func main() {
// vendosja e lidhjes
conn, err := net.Dial("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatalln(err)
}
for {
// leximi i të dhënave nga stdin
reader := bufio.NewReader(os.Stdin)
fmt.Print("Teksti për të dërguar: ")
// rresht pas rreshti
text, err := reader.ReadString('n')
if err != nil {
log.Fatalln(err)
}
// dërgimi
fmt.Fprintf(conn, text+"n")
// pranimi
msg, err := bufio.NewReader(conn).ReadString('n')
if err != nil {
log.Fatalln(err)
}
// printimi i përgjigjes së marrë
fmt.Print("Mesazhi nga Serveri: " + msg)
}
}Përfundim
Mekanizmat që sigurojnë transmetimin e besueshëm të të dhënave dhe përdorimin e tyre
Mekanizmi
Aplikimi, komenti
Kontrolli i shumës
Përdoret për të zbuluar gabimet e bitëve në paketën e dërguar
Të dhëna në pritje
Matja e intervalit të pritjes dhe tregimi i përfundimit të tij. Kjo e fundit tregon se me një shkallë të lartë të sigurisë, paketa ose pranuar e saj është humbur gjatë transmetimit. Në rastin kur paketa arrin me vonesë, por nuk humbet (skadimi i parakohshëm i intervalit të pritjes), ose ndodhin humbje të pranuar, ripërsëritja çon në dyfishim të paketës në anën e pritësit.
Numri rendor
PĂ«rdoret pĂ«r numĂ«rimin rendor tĂ« paketimeve tĂ« dhĂ«nave tĂ« dĂ«rguara nga dĂ«rguesi te marrĂ«si. Ădo ndalesĂ« nĂ« numrat rendor tĂ« paketimeve tĂ« pranuara do tĂ« lejojĂ« marrĂ«sin tĂ« zbulojĂ« humbjen e paketave. Numrat rendor tĂ« njĂ«jtĂ« tĂ« paketave nĂ«nkuptojnĂ« se paketat po e dyfishojnĂ« njĂ«ra-tjetrĂ«n.
Konfirmimi
Generohet nga pala e pranuar dhe i tregon pales dërguese se paketa përkatëse ose grupi i paketave janë pranuar me sukses. Zakonisht konfirmimi përmban numrat rendor të paketave që janë pranuar me sukses. Varësisht nga protokolli, ndahen konfirmimet individuale dhe të grupit.
Konfirmim negativ
Përdoret nga marrësi për t'i raportuar dërguesit se paketa është pranuar gabimisht. Konfirmimi negativ zakonisht përfshin numrin rendor të paketës që nuk është pranuar saktë.
Dërrasa, pipeline
Kufizojnë gamën e numrave rendor që mund të përdoren për transmetimin e paketave. Dërgesa grupore dhe dorëzimi ndihmojnë në rritjen ndjeshëm të kapacitetit të protokolleve krahasuar me modalitetin e pritjes së konfirmimeve. Siç do të shohim, madhësia e dritares mund të llogaritet në bazë të kapaciteteve të pranimit dhe buferizimit të palës pritëse, si dhe nivelit të ngarkesës së rrjetit.
Shembuj të tjerë të përdorimit të Go për punë me rrjetin
NĂ« .
Burimi: habr.com
