Elementele fundamentale ale transferului fiabil de date

Elementele fundamentale ale transferului fiabil de date

Celor care caută Dedicat înțelegerii rețelelor și protocoalelor.

scurt

Articolul discută elementele de bază ale transmiterii de date fiabile, implementează exemple Go, inclusiv UDP și TCP. Bazat pe timp, два, trei și cărțile „Computer Networks. Top-Down Approach”, altfel toată lumea discută doar Tannenbaum și Oliferov.

Protocolul stratului de transport

Oferă o conexiune logică între procesele aplicației care rulează pe diferite gazde. Din perspectiva aplicației, o conexiune logică arată ca un canal care conectează direct procesele.

Elementele fundamentale ale transferului fiabil de date

Protocoale stratului de transport sunt acceptate de sistemele finale, dar nu de routerele de rețea (cu excepția - DPI). Pe partea expeditorului, stratul de transport convertește datele din stratul de aplicație pe care le primește din procesul de trimitere a aplicației în pachete de strat de transport numite segmente.

Elementele fundamentale ale transferului fiabil de date

Acest lucru se face prin împărțirea (dacă este necesar) mesajele stratului de aplicație în fragmente și adăugarea unui antet strat de transport la fiecare dintre ele.

Elementele fundamentale ale transferului fiabil de date

Stratul de transport trece apoi segmentul în stratul de rețea al expeditorului, unde segmentul este încapsulat într-un pachet de strat de rețea (datagramă) și trimis. La capătul de recepție, stratul de rețea extrage segmentul stratului de transport din datagramă și îl transmite în sus la stratul de transport. În continuare, stratul de transport prelucrează segmentul primit, astfel încât datele acestuia să devină disponibile pentru aplicația de recepție.

Elementele fundamentale ale transferului fiabil de date

Principiile transmisiei fiabile a datelor

Transmitere fiabilă a datelor pe un canal complet securizat

Cel mai simplu caz. Partea care trimite pur și simplu primește datele de la stratul superior, creează un pachet care le conține și le trimite către canal.

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 + надежный канал
    }
}

client

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)
    }
}

Transmitere fiabilă a datelor pe un canal cu posibile erori

Următorul pas este să presupunem că toate pachetele transmise sunt primite în ordinea în care au fost trimise, dar biții din ele pot fi corupti din cauza faptului că canalul transmite uneori date cu distorsiuni.

Elementele fundamentale ale transferului fiabil de date

În acest caz, se folosesc următoarele mecanisme:

  • eroare detectata;
  • părere;
  • retransmisie.

Protocoalele de transfer de date fiabile, care au mecanisme similare pentru repetarea transmisiei de mai multe ori, sunt numite protocoale ARQ (Automatic Repeat ReQuest).
În plus, merită luată în considerare posibilitatea unor erori în chitanțe, atunci când partea care primește nu va primi nicio informație despre rezultatele transferului ultimului pachet.
Soluția la această problemă, folosită și în TCP, este adăugarea unui câmp nou la pachetul de date care conține numărul de secvență al pachetului.

Elementele fundamentale ale transferului fiabil de date

Transmiterea de date fiabilă pe un canal nesigur, supus distorsiunii și pierderii pachetelor

Odată cu distorsiunea, din păcate, există pierderi de pachete în rețea.
Și pentru a rezolva această problemă, sunt necesare mecanisme:

  • determinarea faptului de pierdere a pachetelor;
  • re-livrarea pachetelor pierdute către partea care primește.

În plus, pe lângă pierderea coletului, este necesar să se prevadă posibilitatea pierderii bonului sau, în cazul în care nu se pierde nimic, livrarea acestuia cu o întârziere semnificativă. În toate cazurile, se face același lucru: pachetul este retransmis. Pentru a controla timpul, acest mecanism folosește un cronometru cu numărătoare inversă, care vă permite să determinați sfârșitul intervalului de așteptare. Deci in pachet net TPCKeepAlive este setat implicit la 15 secunde:

// defaultTCPKeepAlive is a default constant value for TCPKeepAlive times
// See golang.org/issue/31510
const (
    defaultTCPKeepAlive = 15 * time.Second
)

Partea de trimitere trebuie să pornească un temporizator de fiecare dată când este transmis un pachet (atât prima, cât și a doua oară), să gestioneze întreruperile de la temporizator și să îl oprească.

Deci, ne-am familiarizat cu conceptele cheie ale protocoalelor fiabile de transfer de date:

  • sume de control;
  • numerele de ordine ale pachetelor;
  • cronometre;
  • încasări pozitive și negative.

Dar asta nu este tot!

Protocol de transfer de date fiabil cu pipeline

În varianta pe care am luat-o în considerare deja, protocolul de livrare fiabil este foarte ineficient. Începe să „încetinească” transmisia furnizată de canalul de comunicare pe măsură ce RTT-ul crește. Pentru a-și spori eficiența și pentru a utiliza mai bine capacitatea canalului de comunicație, se utilizează conducte.

Elementele fundamentale ale transferului fiabil de date

Utilizarea conductelor conduce la:

  • creșterea intervalului de numere de secvență, deoarece toate pachetele trimise (cu excepția retransmisiilor) trebuie să fie identificate în mod unic;
  • necesitatea creșterii bufferelor pe părțile de transmisie și de recepție.

Cerințele pentru intervalul de numere de secvență și dimensiunea bufferului depind de acțiunile pe care protocolul le întreprinde ca răspuns la coruperea, pierderea și întârzierea pachetelor. În cazul conductei, există două metode de corectare a erorilor:

  • returnează N pachete înapoi;
  • repetiție selectivă.

Înapoi N pachete - protocol de fereastră glisantă

Elementele fundamentale ale transferului fiabil de date

Expeditorul trebuie să accepte trei tipuri de evenimente:

  • apel printr-un protocol de nivel superior. Când funcția de trimitere a datelor este numită „de sus”, partea de trimitere verifică mai întâi gradul de umplere a ferestrei (adică prezența a N mesaje trimise în așteptarea primirii chitanțelor). Dacă fereastra este goală, se generează și se transmite un nou pachet, iar valorile variabilelor sunt actualizate. În caz contrar, partea de trimitere returnează datele la stratul superior, iar aceasta este o indicație implicită că fereastra este plină. De obicei, stratul superior va încerca să transmită din nou datele după ceva timp. Într-o aplicație reală, expeditorul ar fi probabil fie să tamponeze datele (în loc să le trimită imediat) fie să aibă un mecanism de sincronizare (cum ar fi un semafor sau un steag) care ar permite stratului superior să apeleze funcția de trimitere numai atunci când fereastra este goală. .
  • primind confirmare. În protocol, pentru un pachet cu numărul de secvență N, este emisă o confirmare generală care indică faptul că toate pachetele cu numere de secvență care precedă N au fost primite cu succes.
  • intervalul de așteptare a expirat. Pentru a determina faptele de pierderi și întârzieri ale pachetelor și chitanțelor, protocolul folosește un cronometru. Dacă intervalul de expirare expiră, partea expeditoare retrimite toate pachetele trimise neconfirmate.

Repetarea selectivă

Atunci când dimensiunea ferestrei și produsul de întârziere a transmisiei-propagare sunt mari, un număr mare de pachete poate fi în curs. Într-un astfel de caz, o singură eroare de pachet poate determina retransmiterea unui număr mare de pachete, dintre care majoritatea nu au fost necesare.

Exemplu

Gamă largă teoretic practicile sunt colectate în implementarea practică TCP. Și dacă cineva știe cel mai bine... bun venit.

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"))
    }
}

client

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)
    }
}

Producție

Mecanisme pentru a asigura transferul și utilizarea fiabilă a datelor

mecanism
Aplicație, comentariu

Verificați suma
Folosit pentru a detecta erorile de biți dintr-un pachet transmis

Timer
Numărează invers intervalul de timeout și indică când a expirat. Aceasta din urmă înseamnă că, cu un grad mare de probabilitate, pachetul sau primirea acestuia se pierde în timpul transmisiei. Dacă un pachet este livrat cu întârziere, dar nu este pierdut (expirarea prematură a intervalului de expirare) sau o chitanță este pierdută, retransmiterea duce la un pachet duplicat pe partea de recepție

Număr de serie
Folosit pentru numerotarea secvenţială a pachetelor de date transmise de la expeditor la destinatar. Lacunele în numerele de secvență ale pachetelor primite permit receptorului să detecteze pierderea pachetelor. Aceleași numere de secvență de pachete înseamnă că pachetele sunt duplicate unul față de celălalt

Confirmare
Generat de capătul de recepție și indicând capătului de expediere că pachetul sau grupul de pachete corespunzător a fost primit cu succes. De obicei, confirmarea conține numerele de secvență ale pachetelor primite cu succes. În funcție de protocol, se disting confirmările individuale și de grup

Confirmare negativă
Folosit de destinatar pentru a informa expeditorul că pachetul a fost primit incorect. O confirmare negativă include de obicei numărul de secvență al pachetului care nu a fost primit corect

Fereastra, transport
Limitați gama de numere de secvență care pot fi utilizate pentru a transmite pachete. Multicast și handshake pot crește semnificativ debitul protocolului în comparație cu așteptarea confirmărilor. După cum vom vedea, dimensiunea ferestrei poate fi calculată pe baza capacităților de recepție și tamponare ale capătului de recepție, precum și a nivelului de încărcare a rețelei

Mai multe exemple de utilizare a Go pentru rețele

В depozite.

Sursa: www.habr.com

Adauga un comentariu