Асновы надзейнай перадачы даных

Асновы надзейнай перадачы даных

Тым, хто імкнецца разабрацца ў сетках і пратаколах, прысвячаецца.

Коратка

У артыкуле разглядаюцца асновы надзейнай перадачы даных, рэалізуюцца прыклады на Go, у тым ліку UDP і TCP. Па матывах раз, два, 3 і кнігі "Кампутарныя сеткі. Сыходны падыход", а то ўсё абмяркоўваюць толькі Танэнбаўма і Аліфераў.

Пратакол транспартнага ўзроўню

Забяспечвае лагічнае злучэнне паміж прыкладнымі працэсамі, якія выконваюцца на розных хастах. Лагічнае злучэнне з пункту гледжання прыкладанняў выглядае як канал, які непасрэдна злучае працэсы.

Асновы надзейнай перадачы даных

Пратаколы транспартнага ўзроўню падтрымліваюцца канчатковымі сістэмамі, але не сеткавымі маршрутызатарамі (акрамя - Кропак на цалю). На баку адпраўніка транспартны ўзровень пераўтворыць дадзеныя прыкладнога ўзроўня, якія атрымлівае ад які перадае прыкладнага працэсу, у пакеты транспартнага ўзроўня, званыя сегментамі.

Асновы надзейнай перадачы даных

Гэта робіцца разбіццём (пры неабходнасці) паведамленняў прыкладнога ўзроўня на фрагменты і даданнем да кожнага з іх загалоўка транспартнага ўзроўня.

Асновы надзейнай перадачы даных

Далей транспартны ўзровень перадае сегмент сеткаваму ўзроўню адпраўніка, дзе сегмент інкапсулюецца ў пакет сеткавага ўзроўню (дэйтаграму) і адсылаецца. На які прымае боку сеткавы ўзровень здабывае сегмент транспартнага ўзроўня з дейтаграммы і перадае яго ўверх транспартнаму ўзроўню. Далей транспартны ўзровень апрацоўвае атрыманы сегмент такім чынам, каб яго дадзеныя сталі даступныя з дадаткам-атрымальніку.

Асновы надзейнай перадачы даных

Прынцыпы надзейнай перадачы даных

Надзейная перадача даных па зусім надзейным канале

Найпросты выпадак. Які адпраўляе бок проста прымае дадзеныя ад верхняга ўзроўня, стварае ўтрымоўвальны іх пакет і адпраўляе яго ў канал.

Сервер

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

Кліент

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

Надзейная перадача даных па канале з магчымымі памылкамі

Наступны этап - гэта здагадка, што ўсе перададзеныя пакеты атрыманы ў тым парадку, у якім яны былі адпраўленыя, але біты ў іх могуць быць пашкоджаны, у сувязі з тым, што канал часам перадае дадзеныя з скажэннямі.

Асновы надзейнай перадачы даных

У такім выпадку прымяняюцца механізмы:

  • выяўлення памылкі;
  • зваротнай сувязі;
  • паўторнай перадачы.

Пратаколы надзейнай перадачы дадзеных, якія валодаюць падобнымі механізмамі шматразовага паўтарэння перадачы, называюцца пратаколамі з аўтаматычным запытам паўторнай перадачы (Automatic Repeat reQuest, ARQ).
Дадаткова, варта прадугледзець магчымасць памылак і ў квітанцыях, калі бок, які прымае, не атрымае ніякай інфармацыі аб выніках перадачы апошняга пакета.
Рашэнне гэтай задачы, выкарыстоўванае ў тым ліку ў TCP, складаецца ў даданні ў пакет дадзеных новага поля, утрымоўвальнага парадкавы нумар пакета.

Асновы надзейнай перадачы даных

Надзейная перадача даных па ненадзейным канале, які дапускае скажэнне і страту пакетаў

Адначасова з скажэннямі, нажаль, у сетцы прысутнічае страта пакетаў.
І для рашэння гэтай задачы патрабуюцца механізмы:

  • вызначэння факту страты пакетаў;
  • паўторнай дастаўкі страчаных пакетаў прымаючаму боку.

Дадаткова, акрамя страты пакета, неабходна прадугледзець магчымасць страты квітанцыі ці, калі нічога не страчана, яе дастаўкі са значнай затрымкай. Ва ўсіх выпадках вырабляецца адно і тое ж: паўторная перадача пакета. Для кантралявання часу ў дадзеным механізме выкарыстоўваецца таймер адліку, які дазваляе вызначыць канчатак інтэрвалу чакання. Так у пакеце сетка параметр TCPKeepAlive усталяваны на 15 секунд па змаўчанні:

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

Які перадае боку неабходна запускаць таймер кожны раз пры перадачы пакета (як пры першай, так і пры паўторнай), апрацоўваць перапыненні ад таймера і спыняць яго.

Такім чынам, мы азнаёміліся з ключавымі паняццямі пратаколаў надзейнай перадачы даных:

  • кантрольнымі сумамі;
  • парадкавымі нумарамі пакетаў;
  • таймерамі;
  • станоўчымі і адмоўнымі квітанцыямі.

Але і гэта не ўсё!

Пратакол надзейнай перадачы дадзеных з канвеерызацыяй

У тым варыянце, які мы ўжо разгледзелі, пратакол надзейнай дастаўкі вельмі неэфектыўны. Ён пачынае тармазіць перадачу, якая забяспечваецца каналам сувязі, пры павелічэнні RTT. Для падвышэння яго эфектыўнасці, лепшай утылізацыі прапускной здольнасці канала сувязі ўжываюць канвеерызацыю.

Асновы надзейнай перадачы даных

Ужыванне канвеерызацыі прыводзіць да:

  • павелічэнню дыяпазону парадкавых нумароў, паколькі ўсе пакеты, якія адсылаюцца (за выключэннем паўторных перадач) павінны быць адназначна ідэнтыфікуемыя;
  • неабходнасці ў павелічэнні буфераў на якая перадае і прымаючага баках.

Дыяпазон парадкавых нумароў і патрабаванні да памераў буфераў залежаць ад дзеянняў, якія прымаюцца пратаколам у адказ на скажэнне, страту і затрымку пакета. У выпадку канвеерызацыі існуюць два метады выпраўлення памылак:

  • вяртанне на N пакетаў таму;
  • выбарачнае паўтарэнне.

Вяртанне на N пакетаў назад - пратакол слізгальнага акна

Асновы надзейнай перадачы даных

Адпраўнік павінен падтрымліваць тры тыпы падзей:

  • выклік пратаколам больш высокага ўзроўню. Калі "зверху" выклікаецца функцыя адпраўкі дадзеных, які перадае бок спачатку правярае ступень запаўнення акна (гэта значыць наяўнасць N пасланых паведамленняў, якія чакаюць атрымання квітанцый). Калі акно апыняецца незапоўненым, новы пакет фармуецца і перадаецца, а значэнні зменных абнаўляюцца. У адваротным выпадку які перадае бок вяртае дадзеныя верхняму ўзроўню, і гэта з'яўляецца няяўным указаннем, што акно запоўнена. Звычайна верхні ўзровень прадпрымае паўторную спробу перадачы дадзеных праз некаторы час. У рэальным дадатку адпраўнік, хутчэй за ўсё, або буферызаваў бы дадзеныя (замест неадкладнай дасылкі), або меў механізм сінхранізацыі (напрыклад, семафор або сцяг), які дазваляў бы вышэйстаячаму ўзроўню выклікаць функцыю адпраўкі дадзеных толькі пры незапоўненым акне.
  • атрыманне пацверджання. У пратаколе для пакета з парадкавым нумарам N выдаецца агульная квітанцыя, якая паказвае на тое, што ўсе пакеты з парадкавымі нумарамі, якія папярэднічалі N, паспяхова прыняты.
  • заканчэнне інтэрвалу чакання. Для вызначэння фактаў страт і затрымак пакетаў і квітанцый пратакол выкарыстоўвае таймер. Калі інтэрвал чакання заканчваецца, які перадае бок паўторна адпраўляе ўсе пасланыя непацверджаныя пакеты.

Выбарачнае паўтарэнне

Калі памер акна і твор прапускной здольнасці на затрымку распаўсюджвання вялікія, у канвееры можа знаходзіцца вялікая колькасць пакетаў. У такім выпадку памылка асобнага пакета можа выклікаць паўторную перадачу вялікай колькасці пакетаў, большасць з якіх не патрабаваліся.

Прыклад

Лепшыя тэарытычныя практыкі сабраны ў практычнай рэалізацыі TCP. А калі хтосьці ведае, як лепш - дабро запрашаем.

Сервер

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

Кліент

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

Выснова

Механізмы, якія забяспечваюць надзейную перадачу даных і іх выкарыстанне

механізм
Ужыванне, каментар

Кантрольная сума
Выкарыстоўваецца для выяўлення бітавых памылак у перададзеным пакеце

таймер
Адлік інтэрвалу чакання і ўказанне на яго заканчэнне. Апошняе азначае, што з высокай ступенню верагоднасці пакет ці яго квітанцыя страчаны пры перадачы. У выпадку, калі пакет дастаўляецца з затрымкай, але не губляецца (дачаснае заканчэнне інтэрвалу чакання), альбо адбываецца страта квітанцыі, паўторная перадача прыводзіць да дублявання пакета на боку, які прымае.

Парадкавы нумар
Выкарыстоўваецца для парадкавай нумарацыі пакетаў дадзеных, якія перадаюцца ад адпраўніка да атрымальніка. Парывы ​​ў парадкавых нумарах атрыманых пакетаў дазваляюць атрымальніку выявіць страту пакета. Аднолькавыя парадкавыя нумары пакетаў азначаюць, што пакеты дублююць адзін аднаго.

Пацвярджэнне
Генеруецца які прымае бокам і паказвае які перадае боку на тое, што адпаведны пакет або група пакетаў паспяхова прыняты. Звычайна пацвярджэнне змяшчае парадкавыя нумары паспяхова прынятых пакетаў. У залежнасці ад пратакола адрозніваюць індывідуальныя і групавыя пацверджанні

Адмоўнае пацверджанне
Выкарыстоўваецца атрымальнікам для паведамлення адпраўніку, што пакет быў атрыманы некарэктна. Адмоўнае пацверджанне звычайна складаецца з парадкавы нумар пакета, які не быў карэктна атрыманы.

Акно, канвеерызацыя
Абмяжоўваюць дыяпазон парадкавых нумароў, якія могуць выкарыстоўвацца для перадачы пакетаў. Групавая перадача і поціск рукі дазваляюць значна павялічыць прапускную здольнасць пратаколаў у параўнанні з рэжымам чакання пацверджанняў. Як мы ўбачым, памер акна можа быць разлічаны на аснове магчымасцяў прыёму і буферызацыі прымаючага боку, а таксама ўзроўню загрузкі сеткі

Іншыя прыклады выкарыстання Go для працы з сеткай

В рэпазітары.

Крыніца: habr.com

Дадаць каментар