Al tiuj, kiuj
Mallonge
La artikolo diskutas la bazojn de fidinda datumtranssendo, efektivigas ekzemplojn pri
Protokolo pri transporta tavolo
Provizas logikan ligon inter aplikaĵprocezoj kurantaj sur malsamaj gastigantoj. De aplika perspektivo, logika konekto aspektas kiel kanalo, kiu rekte ligas procezojn.
Ĉi tio estas farita disigante (se necese) la aplikaĵtavolmesaĝojn en fragmentojn kaj aldonante transporttavolan kaplinion al ĉiu el ili.
La transporttavolo tiam pasas la segmenton al la rettavolo de la sendinto, kie la segmento estas enkapsuligita en rettavolpakaĵeto (datagramo) kaj sendita. Ĉe la riceva fino, la rettavolo ĉerpas la transporttavolsegmenton de la datagramo kaj pasas ĝin supren al la transporttavolo. Poste, la transporttavolo prilaboras la ricevitan segmenton tiel ke ĝiaj datenoj iĝas haveblaj al la ricevanta aplikaĵo.
Principoj de fidinda transdono de datumoj
Fidinda transdono de datumoj per tute sekura kanalo
La plej simpla kazo. La senda flanko simple ricevas la datumojn de la supra tavolo, kreas pakaĵeton enhavantan ĝin, kaj sendas ĝin al la kanalo.
Servilo
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 + надежный канал
}
}
Kliento
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)
}
}
Fidinda transdono de datumoj tra kanalo kun eblaj eraroj
La sekva paŝo estas supozi, ke ĉiuj elsenditaj pakoj estas ricevitaj en la ordo en kiu ili estis senditaj, sed la bitoj en ili povas esti koruptitaj pro la fakto, ke la kanalo foje transdonas datumojn kun misprezentoj.
En ĉi tiu kazo, la sekvaj mekanismoj estas uzataj:
- erardetekto;
- retrosciigo;
- retranssendo.
Fidindaj datumtransigo-protokoloj kiuj havas similajn mekanismojn por ripetado de dissendo plurfoje estas nomitaj Automatic Repeat reQuest (ARQ) protokoloj.
Aldone, indas konsideri la eblecon de eraroj en kvitancoj, kiam la ricevanto ne ricevos informojn pri la rezultoj de la translokigo de la lasta pako.
La solvo al ĉi tiu problemo, ankaŭ uzata en TCP, estas aldoni novan kampon al la datumpakaĵo enhavanta la sinsekvon de la pako.
Fidinda datumtranssendo tra nefidinda kanalo kondiĉigita de pakaĵeto misprezento kaj perdo
Kune kun distordo, bedaŭrinde, estas paka perdo en la reto.
Kaj por solvi ĉi tiun problemon, necesas mekanismoj:
- determini la fakton de paka perdo;
- retransdono de perditaj pakaĵoj al la ricevanto.
Aldone, krom la perdo de la pako, necesas antaŭvidi la eblecon de perdo de la kvitanco aŭ, se nenio estas perdita, ĝian liveron kun grava prokrasto. En ĉiuj kazoj oni faras la samon: la pako estas retransdonata. Por kontroli tempon, ĉi tiu mekanismo uzas retronombradon, kiu permesas vin determini la finon de la atenda intervalo. Do en la pakaĵo
// defaultTCPKeepAlive is a default constant value for TCPKeepAlive times
// See golang.org/issue/31510
const (
defaultTCPKeepAlive = 15 * time.Second
)
La sendanta flanko devas komenci tempigilon ĉiufoje kiam pakaĵeto estas elsendita (kaj la unua kaj la duan fojon), trakti interrompojn de la tempigilo kaj haltigi ĝin.
Do, ni konatiĝis kun la ŝlosilaj konceptoj de fidindaj datumtransigo-protokoloj:
- ĉeksumoj;
- sinsekvaj nombroj de pakoj;
- temporiziloj;
- pozitivaj kaj negativaj kvitancoj.
Sed tio ne estas ĉio!
Fidinda datumtransiga protokolo kun dukto
En la varianto, kiun ni jam konsideris, la fidinda livera protokolo estas tre malefika. Ĝi komencas "malrapidigi" la transdonon provizitan de la komunika kanalo dum la RTT pliiĝas. Por pliigi ĝian efikecon kaj pli bone utiligi la komunikadkanalan kapaciton, dukto estas uzata.
La uzo de dukto kondukas al:
- pliigante la gamon de sekvencnombroj, ĉar ĉiuj senditaj pakaĵetoj (krom redissendoj) devas esti unike identigitaj;
- la bezono pliigi bufrojn sur la elsendaj kaj ricevantaj flankoj.
La sekvencnumerintervalo kaj bufrograndecpostuloj dependas de la agoj kiujn la protokolo prenas en respondo al pakaĵetkorupto, perdo, kaj prokrasto. En la kazo de dukto, ekzistas du metodoj por korekti erarojn:
- resendi N pakojn;
- selektema ripeto.
Reiri N pakojn - glitfenestra protokolo
La sendinto devas subteni tri specojn de eventoj:
- voki per pli alta nivela protokolo. Kiam la funkcio de sendo de datumoj nomiĝas "de supre", la senda flanko unue kontrolas la gradon de plenigo de la fenestro (tio estas, la ĉeesto de N senditaj mesaĝoj atendantaj ricevon de kvitancoj). Se la fenestro estas malplena, nova pako estas generita kaj transdonita, kaj la variaj valoroj estas ĝisdatigitaj. Alie, la senda flanko resendas datumojn al la supra tavolo, kaj ĉi tio estas implica indiko, ke la fenestro estas plena. Tipe la supra tavolo provos transdoni la datumojn denove post iom da tempo. En reala aplikiĝo, la sendinto verŝajne aŭ bufrus la datenojn (anstataŭ sendi ĝin tuj) aŭ havus sinkronigan mekanismon (kiel ekzemple semaforo aŭ flago) kiu permesus al la supra tavolo voki la sendan funkcion nur kiam la fenestro estas malplena. .
- ricevante konfirmon. En la protokolo, por pakaĵeto kun sekvencnumero N, ĝenerala agnosko estas emisiita indikante ke ĉiuj pakaĵetoj kun sekvencnombroj antaŭantaj N estis sukcese ricevitaj.
- la atenda intervalo eksvalidiĝis. Por determini la faktojn de perdoj kaj malfruoj de pakoj kaj kvitancoj, la protokolo uzas tempigilon. Se la tempintervalo eksvalidiĝas, la senda flanko resendas ĉiujn senditajn neagnoskitajn pakaĵetojn.
Selektema ripeto
Kiam la fenestra grandeco kaj la trai-disvastiga prokrasto-produkto estas grandaj, granda nombro da pakaĵetoj povas esti en la dukto. En tia kazo, ununura paka eraro povas kaŭzi grandan nombron da pakaĵetoj esti retranssenditaj, la plej multaj el kiuj ne estis postulataj.
Ekzemplo:
La plej bona
Servilo
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"))
}
}
Kliento
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)
}
}
konkludo
Mekanismoj por certigi fidindan translokigon kaj uzon de datumoj
Механизм
Apliko, komento
Kontrola sumo
Uzita por detekti bitajn erarojn en elsendita pakaĵeto
Tempigilo
Nombras malsupren la paŭzintervalo kaj indikas kiam ĝi eksvalidiĝis. Ĉi-lasta signifas, ke kun alta grado de probableco la pakaĵo aŭ ĝia kvitanco perdiĝas dum transdono. Se pakaĵeto estas liverita kun prokrasto, sed ne estas perdita (malfrua eksvalidiĝo de la tempo-intervalo), aŭ kvitanco estas perdita, retranssendo kondukas al duplikata pakaĵeto ĉe la ricevanta flanko.
Seria numero
Uzite por sinsekva numerado de datumpakaĵoj elsenditaj de sendinto ĝis ricevanto. Interspacoj en la sekvencnombroj de ricevitaj pakaĵetoj permesas al la ricevilo detekti pakaĵetperdon. La samaj pakaj sekvencnombroj signifas ke la pakaĵetoj estas duplikatoj unu de la alia
Konfirmo
Generita de la ricevanta fino kaj indikanta al la sendanta fino ke la responda pakaĵeto aŭ grupo de pakoj estis sukcese ricevita. Tipe la agnosko enhavas la sekvencnombrojn de sukcese ricevitaj pakaĵetoj. Depende de la protokolo oni distingas individuajn kaj grupajn konfirmojn
Negativa konfirmo
Uzita de la ricevanto por informi la sendinton, ke la pako estis ricevita malĝuste. Negativa agnosko kutime inkluzivas la sinsekvon de la pakaĵeto kiu ne estis ĝuste ricevita
Window, conveyorization
Limigu la gamon de sinsekvaj nombroj, kiuj povas esti uzataj por transdoni pakaĵojn. Multicast kaj manpremo povas signife pliigi protokolan trairon kompare kun atendado de agnoskoj. Kiel ni vidos, la fenestra grandeco povas esti kalkulita surbaze de la ricevaj kaj bufraj kapabloj de la riceva fino, same kiel la reta ŝarĝnivelo.
Pliaj ekzemploj de uzado de Go por interkonektado
В
fonto: www.habr.com