
Niille, jotka Omistettu verkkojen ja protokollien ymmärtämiseen.
lyhyesti
Artikkelissa käsitellään luotettavan tiedonsiirron perusteita ja esitetään esimerkkejä siitä, , mukaan lukien UDP ja TCP. Perustuu , , ja kirja "Tietokoneverkot: Ylhäältä alas -lähestymistapa", muuten kaikki keskustelevat vain Tannenbaumista ja Oliferovista.
Siirtokerroksen protokolla
Tarjoaa loogisen yhteyden eri isäntäkoneilla suoritettavien sovellusprosessien välille. Sovellusten näkökulmasta looginen yhteys näkyy kanavana, joka yhdistää prosesseja suoraan.

päätejärjestelmien tukema, mutta ei verkkoreitittimien (paitsi - Lähettäjän puolella siirtokerros muuntaa lähettävältä sovellusprosessilta vastaanottamansa sovelluskerroksen tiedot siirtokerroksen paketeiksi, joita kutsutaan segmenteiksi.

Tämä tehdään jakamalla (tarvittaessa) sovelluskerroksen viestit fragmentteihin ja lisäämällä siirtokerroksen otsikko jokaiseen fragmenttiin.

Siirtokerros välittää sitten segmentin lähettäjän verkkokerrokselle, jossa se kapseloidaan verkkokerroksen pakettiin (datagrammiin) ja lähetetään. Vastaanottavassa päässä verkkokerros poimii siirtokerroksen segmentin datagrammista ja välittää sen siirtokerrokselle. Siirtokerros käsittelee sitten vastaanotetun segmentin niin, että sen tiedot ovat vastaanottavan sovelluksen käytettävissä.

Turvallisen tiedonsiirron periaatteet
Luotettava tiedonsiirto täysin suojatun kanavan kautta
Yksinkertaisin tapaus: Lähettävä puoli yksinkertaisesti vastaanottaa dataa ylemmältä kerrokselta, luo sitä sisältävän paketin ja lähettää sen kanavaa pitkin.
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 + надежный канал
}
}asiakas
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)
}
}Luotettava tiedonsiirto kanavan yli, jossa on mahdollisia virheitä
Seuraava vaihe on olettaa, että kaikki lähetetyt paketit vastaanotetaan lähetysjärjestyksessä, mutta niiden bitit voivat olla vioittuneet, koska kanava lähettää joskus vääristyneitä tietoja.

Tässä tapauksessa käytetään seuraavia mekanismeja:
- virheiden havaitseminen;
- palaute;
- uudelleenlähetys.
Luotettavia tiedonsiirtoprotokollia, joilla on tällaiset mekanismit useille lähetystoistoille, kutsutaan Automatic Repeat ReQuest (ARQ) -protokolliksi.
Lisäksi kannattaa ottaa huomioon kuittien virheiden mahdollisuus, kun vastaanottava osapuoli ei saa mitään tietoa viimeisimmän paketin siirron tuloksista.
Tämän ongelman ratkaisu, jota käytetään muun muassa TCP:ssä, on lisätä datapakettiin uusi kenttä, joka sisältää paketin järjestysnumeron.

Luotettava tiedonsiirto epäluotettavan kanavan kautta, joka mahdollistaa pakettien vääristymisen ja katoamisen
Vääristymien ohella verkossa valitettavasti esiintyy pakettien katoamista.
Ja tämän ongelman ratkaisemiseksi tarvitaan mekanismeja:
- pakettien katoamisen tosiasian määrittäminen;
- kadonneiden pakettien uudelleentoimitus vastaanottajalle.
Pakettihävikin lisäksi on otettava huomioon myös kuitin katoamisen mahdollisuus tai, jos mitään ei ole kadonnut, sen toimittaminen merkittävällä viiveellä. Kaikissa tapauksissa tapahtuu sama toimenpide: paketti lähetetään uudelleen. Ajoituksen hallitsemiseksi tämä mekanismi käyttää lähtölaskenta-ajastinta, jonka avulla voidaan määrittää odotusajan loppu. Näin ollen paketissa TCPKeepAlive on oletusarvoisesti asetettu arvoon 15 sekuntia:
// defaultTCPKeepAlive is a default constant value for TCPKeepAlive times
// See golang.org/issue/31510
const (
defaultTCPKeepAlive = 15 * time.Second
)Lähettävän puolen on käynnistettävä ajastin joka kerta, kun paketti lähetetään (sekä ensimmäisen että toisen kerran), käsiteltävä ajastimen keskeytykset ja pysäytettävä se.
Olemme siis tutustuneet luotettavien tiedonsiirtoprotokollien keskeisiin käsitteisiin:
- tarkistussummat;
- pakkausten sarjanumerot;
- ajastimet;
- positiiviset ja negatiiviset tulot.
Mutta siinä ei ole kaikki!
Luotettava tiedonsiirtoprotokolla putkistolla
Edellä tarkastelemassamme toteutuksessa luotettava toimitusprotokolla on erittäin tehoton. Se alkaa "hidastaa" tietoliikennekanavan tarjoamaa lähetystä RTT:n kasvaessa. Tehokkuuden parantamiseksi ja tietoliikennekanavan kaistanleveyden hyödyntämiseksi käytetään liukuhihnatekniikkaa (pipelining).

Putkiston käyttö johtaa:
- järjestysnumeroiden alueen lisääminen, koska kaikkien lähetettyjen pakettien (uudelleenlähetyksiä lukuun ottamatta) on oltava yksilöllisesti tunnistettavissa;
- tarve lisätä puskureita lähetys- ja vastaanottopuolella.
Sarjanumeroiden vaihteluväli ja puskurin kokovaatimukset riippuvat protokollan reaktiosta pakettien vioittumiseen, katoamiseen ja viiveeseen. Liukuhihnakäytössä on kaksi virheenkorjausmenetelmää:
- palauta N pakettia takaisin;
- valikoiva toisto.
Taaksepäin N pakettia - liukuvan ikkunan protokolla

Lähettäjän on tuettava kolmenlaisia tapahtumia:
- Ylemmän tason protokollan kutsu. Kun lähetysfunktiota kutsutaan ylhäältä, lähettäjä tarkistaa ensin, onko ikkuna täynnä (eli onko N lähetettyä viestiä odottamassa kuittausta). Jos ikkuna ei ole täynnä, muodostetaan ja lähetetään uusi paketti, ja muuttujien arvot päivitetään. Muussa tapauksessa lähettäjä palauttaa tiedot ylemmälle kerrokselle, mikä on implisiittinen osoitus siitä, että ikkuna on täynnä. Tyypillisesti ylempi kerros yrittää tiedon lähettämistä uudelleen jonkin ajan kuluttua. Todellisessa sovelluksessa lähettäjä todennäköisesti joko puskuroisi tiedot (sen sijaan, että lähettäisi ne välittömästi) tai sillä olisi synkronointimekanismi (esim. semafori tai lippu), joka sallisi ylemmän kerroksen kutsua lähetysfunktiota vain, kun ikkuna ei ole täynnä.
- Kuittauksen vastaanottaminen. Protokollassa annetaan yleinen kuittaus järjestysnumeroltaan N olevalle paketille, joka osoittaa, että kaikki N:ää edeltävät järjestysnumerot sisältävät paketit on vastaanotettu onnistuneesti.
- Aikakatkaisun umpeutuminen. Protokolla käyttää ajastinta pakettien ja kuittausten katoamisen ja viiveiden havaitsemiseen. Jos aikakatkaisu umpeutuu, lähettävä osapuoli lähettää uudelleen kaikki kuittaamattomat paketit.
Valikoiva toisto
Kun ikkunan koko ja läpäisykyvyn ja viiveen tulo ovat suuria, putkessa voi olla suuri määrä paketteja. Tässä tapauksessa yhden paketin virhe voi aiheuttaa suuren määrän pakettien uudelleenlähetyksen, joista useimpia ei tarvittu.
Esimerkki
Toppi käytäntöjä kerätään käytännön toteutuksessa Ja jos joku tietää paremman tavan... .
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"))
}
}asiakas
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)
}
}johtopäätös
Mekanismit, jotka varmistavat luotettavan tiedonsiirron ja käytön
Механизм
Sovellus, kommentti
Tarkista summa
Käytetään lähetetyn paketin bittivirheiden havaitsemiseen.
ajastin
Aikakatkaisuvälin lähtölaskenta ja sen umpeutumisen ilmaisin. Jälkimmäinen osoittaa, että paketti tai sen kuittaus on suurella todennäköisyydellä kadonnut lähetyksen aikana. Jos paketti toimitetaan myöhässä, mutta ei ole kadonnut (aikakatkaisuvälin ennenaikainen umpeutuminen) tai kuittaus katoaa, uudelleenlähetys johtaa paketin kaksoiskappaleeseen vastaanottavassa päässä.
Sarjanumero
Käytetään lähettäjältä vastaanottajalle lähetettyjen datapakettien järjestysnumerointiin. Vastaanotettujen pakettien järjestysnumeroiden aukot mahdollistavat vastaanottajan pakettien katoamisen havaitsemisen. Identtiset pakettien järjestysnumerot osoittavat kaksoispaketteja.
vahvistus
Vastaanottava osapuoli luo tämän kuitin, joka ilmoittaa lähettävälle osapuolelle, että vastaava paketti tai pakettiryhmä on vastaanotettu onnistuneesti. Kuittaukset sisältävät tyypillisesti onnistuneesti vastaanotettujen pakettien järjestysnumerot. Protokollasta riippuen erotetaan toisistaan yksittäiset ja ryhmäkuittaukset.
Negatiivinen vahvistus
Vastaanottaja käyttää tätä ilmoittaakseen lähettäjälle, että pakettia ei vastaanotettu oikein. Negatiivinen kuittaus sisältää tyypillisesti sen paketin järjestysnumeron, jota ei vastaanotettu oikein.
Ikkuna, putkisto
Ne rajoittavat pakettien lähetyksessä käytettävien järjestysnumeroiden valikoimaa. Monilähetys ja kättely lisäävät merkittävästi protokollan läpäisykykyä verrattuna kuittausten odottamiseen. Kuten näemme, ikkunan koko voidaan laskea vastaanottavan pään vastaanotto- ja puskurointiominaisuuksien sekä verkon kuormituksen perusteella.
Muita esimerkkejä Go:n käytöstä verkostoitumiseen
В .
Lähde: will.com
