Bazat e transmetimit të sigurshëm të të dhënave

Bazat e transmetimit të sigurshëm të të dhënave

Atyre që synon meritojnë të kuptojnë rrjetet dhe protokollet.

Shkurt

Ky artikull shqyrton bazat e transmetimit të sigurt të të dhënave, duke realizuar shembuj në Go, duke përfshirë UDP dhe TCP. Në frymën e një herë, dy herë, tri herë 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.

Bazat e transmetimit të sigurshëm të të dhënave

Protokollat e nivelit transportit mbĂ«shteten nga sistemet pĂ«rfundimtare, por jo nga rrugĂ«tuesit e rrjetit (pĂ«rveç — DPI). 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.

Bazat e transmetimit të sigurshëm të të dhënave

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.

Bazat e transmetimit të sigurshëm të të dhënave

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.

Bazat e transmetimit të sigurshëm të të dhënave

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.

Bazat e transmetimit të sigurshëm të të dhënave

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.

Bazat e transmetimit të sigurshëm të të dhënave

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ë net 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.

Bazat e transmetimit të sigurshëm të të dhënave

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

Bazat e transmetimit të sigurshëm të të dhënave

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 teorike tĂ« mbledhura nĂ« njĂ« implementim praktik TCP. Dhe nĂ«se dikush di njĂ« mĂ«nyrĂ« mĂ« tĂ« mirĂ« — mirĂ«sevini.

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Ă« repozitorit.

Burimi: habr.com

Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster