À quelli chì
Brevemente
L'articulu discute i principii di a trasmissione di dati affidabile, implementa esempii
Protocolu di a strata di trasportu
Fornisce una cunnessione logica trà i prucessi di l'applicazione in esecuzione in diversi host. Da una perspettiva di l'applicazione, una cunnessione logica s'assumiglia à un canale chì cunnetta direttamente i prucessi.
Questu hè fattu splitting (se necessariu) i missaghji di a capa di l'applicazione in frammenti è aghjunghjendu un capu di capa di trasportu à ognunu di elli.
A strata di trasportu poi passa u segmentu à a capa di rete di u mittente, induve u segmentu hè incapsulatu in un pacchettu di strata di rete (datagramma) è mandatu. À a fine di riceve, a strata di a rete estrae u segmentu di a capa di trasportu da u datagramma è u passa à a capa di trasportu. In seguitu, a capa di trasportu processa u segmentu ricivutu in modu chì e so dati diventanu dispunibuli per l'applicazione chì riceve.
Principii di trasmissione di dati affidabile
Trasmissione di dati affidabile nantu à un canale cumpletamente sicuru
U casu più simplice. U latu di l'inviu riceve simpricimenti i dati da a capa superiore, crea un pacchettu chì cuntene, è u manda à u canali.
Servidor
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 + надежный канал
}
}
Cliente
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)
}
}
Trasmissione di dati affidabile nantu à un canale cù pussibuli errori
U prossimu passu hè di assume chì tutti i pacchetti trasmessi sò ricivuti in l'ordine in quale sò stati mandati, ma i bits in elli ponu esse currutti per u fattu chì u canali trasmette qualchì volta dati cù distorsioni.
In stu casu, i seguenti miccanismi sò usati:
- rilevazione di errore;
- feedback;
- ritrasmissione.
I protokolli di trasferimentu di dati affidabili chì anu meccanismi simili per a ripetizione di a trasmissione parechje volte sò chjamati protokolli Automatic Repeat reQuest (ARQ).
Inoltre, vale a pena cunsiderà a pussibilità di l'errore in i ricivuti, quandu u partitu chì riceve ùn riceve micca infurmazione nantu à i risultati di u trasferimentu di l'ultimu pacchettu.
A suluzione à stu prublema, utilizata ancu in TCP, hè di aghjunghje un novu campu à u pacchettu di dati chì cuntene u numeru di sequenza di u pacchettu.
Trasmissione di dati affidabile nantu à un canale inaffidabile sottumessu à distorsioni è pèrdite di pacchetti
Inseme à a distorsione, sfurtunatamenti, ci hè una perdita di pacchetti in a reta.
È per risolve stu prublema, i miccanismi sò necessarii:
- determinà u fattu di perdita di pacchetti;
- ri-consegna di pacchetti persi à a parte ricevente.
Inoltre, in più di a perdita di u pacchettu, hè necessariu di furnisce a pussibilità di perdita di u ricivutu o, se nunda hè persu, a so consegna cun un ritardu significativu. In tutti i casi, a listessa cosa hè fatta: u pacchettu hè ritrasmesso. Per cuntrullà u tempu, stu mecanismu usa un cronometru di countdown, chì permette di determinà a fine di l'intervallu di attesa. Allora in u pacchettu
// defaultTCPKeepAlive is a default constant value for TCPKeepAlive times
// See golang.org/issue/31510
const (
defaultTCPKeepAlive = 15 * time.Second
)
U latu di l'inviu hà bisognu di inizià un timer ogni volta chì un pacchettu hè trasmessu (sia a prima è a seconda volta), gestisce l'interruzzioni da u timer è ferma.
Dunque, avemu familiarizatu cù i cuncetti chjave di protokolli di trasferimentu di dati affidabili:
- checksums;
- numeri di sequenza di pacchetti;
- timers;
- ricevuti pusitivi è negativi.
Ma ùn hè micca tuttu !
Protokollu di trasferimentu di dati affidabile cù pipelining
In a variante chì avemu digià cunsideratu, u protocolu di consegna affidabile hè assai inefficiente. Accumincia à "rallentà" a trasmissione furnita da u canali di cumunicazione cum'è u RTT aumenta. Per aumentà a so efficienza è aduprà megliu a capacità di u canali di cumunicazione, u pipelining hè utilizatu.
L'usu di pipeline porta à:
- aumentà a gamma di numeri di sequenza, postu chì tutti i pacchetti mandati (eccettu per i ritrasmissioni) deve esse identificatu unicu;
- a necessità di aumentà i buffers nantu à i lati di trasmissione è di ricezione.
A gamma di numeri di sequenza è i requisiti di dimensione di u buffer dipendenu da l'azzioni chì u protocolu piglia in risposta à a corruzzione di pacchetti, a perdita è u ritardu. In u casu di pipelining, ci sò dui metudi per correggere l'errore:
- riturnà N pacchetti;
- ripetizione selettiva.
Riturnà N pacchetti - protocolu di finestra scorrevule
U mittente deve sustene trè tippi di avvenimenti:
- chjamate da un protocolu di livellu più altu. Quandu a funzione di mandatu di dati hè chjamata "da sopra", u latu di l'inviu verifica prima u gradu di riempimentu di a finestra (vale à dì, a prisenza di N mandati missaghji in attesa di ricivutu di ricevute). Se a finestra hè viota, un novu pacchettu hè generatu è trasmessu, è i valori variabili sò aghjurnati. Altrimenti, u latu di l'inviu torna dati à a capa superiore, è questu hè un indicazione implicita chì a finestra hè piena. Di genere, a capa superiore pruverà à trasmette i dati di novu dopu qualchì tempu. In una vera applicazione, u mittente prubabilmente o buffer i dati (invece di mandà immediatamente) o avè un mecanismu di sincronizazione (cum'è un semaforu o bandiera) chì permettenu à a capa superiore di chjamà a funzione di mandà solu quandu a finestra hè viota. .
- riceve cunferma. In u protocolu, per un pacchettu cù numeri di sequenza N, un ricunniscenza generale hè emessu chì indica chì tutti i pacchetti cù numeri di sequenza chì precedenu N sò stati ricevuti bè.
- l'intervallu d'attesa hè scadutu. Per determinà i fatti di pèrdite è ritardi di pacchetti è ricevuti, u protocolu usa un cronometru. Se l'intervallu di timeout scade, u latu di l'inviu rinvia tutti i pacchetti mandati micca ricunnisciuti.
Ripetizione selettiva
Quandu a dimensione di a finestra è u produttu di ritardu di propagazione di u throughput sò grande, un gran numaru di pacchetti pò esse in u pipeline. In un tali casu, un unicu errore di pacchettu pò causà un gran numaru di pacchetti per esse ritrasmessi, a maiò parte di i quali ùn eranu micca necessariu.
Esempiu:
U megliu
Servidor
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"))
}
}
Cliente
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)
}
}
cunchiusioni
Meccanismi per assicurà u trasferimentu di dati affidabile è l'usu
Mechanismu
Applicazione, cumentu
Verificate a somma
Adupratu per detect errori di bit in un pacchettu trasmessu
Timer
Cunta l'intervallu di timeout è indica quandu hè scadutu. L'ultime significa chì cù un altu gradu di probabilità u pacchettu o u so ricivutu hè persu durante a trasmissione. Se un pacchettu hè mandatu cù un ritardu, ma ùn hè micca persu (scadenza prematura di l'intervallu di timeout), o una ricevuta hè persa, a ritrasmissione porta à un pacchettu duplicatu da u latu di riceve.
Numeru d'ordine
Adupratu per a numerazione sequenziale di pacchetti di dati trasmessi da u mittente à u destinatariu. Lacune in i numeri di sequenza di pacchetti ricevuti permettenu à u receptore di detectà a perdita di pacchetti. I stessi numeri di sequenza di pacchetti significanu chì i pacchetti sò duplicati l'una di l'altru
Cunfirmazione
Generatu da l'estremità ricevente è indicà à l'estremità di l'inviu chì u pacchettu currispundente o gruppu di pacchetti hè statu ricevutu bè. Di genere, a ricunniscenza cuntene i numeri di sequenza di pacchetti ricevuti successu. Sicondu u protocolu, cunfirmazioni individuali è di gruppu sò distinti
Cunfirmazione negativa
Adupratu da u destinatariu per informà u mittente chì u pacchettu hè statu ricevutu incorrectamente. Un ricunniscenza negativu generalmente include u numeru di sequenza di u pacchettu chì ùn hè micca ricevutu currettamente
Finestra, trasportu
Limite a gamma di numeri di sequenza chì ponu esse utilizati per trasmette pacchetti. Multicast è handshake ponu aumentà significativamente u throughput di u protocolu cumparatu cù l'aspittà di ricunniscenza. Comu avemu vistu, a dimensione di a finestra pò esse calculata in basa di e capacità di ricezione è di buffering di u destinatariu, è ancu di u livellu di carica di a rete.
Più esempi di usu Go per networking
В
Source: www.habr.com