Основи на надеждния трансфер на данни

Основи на надеждния трансфер на данни

На тези, които търси Посветен на разбирането на мрежи и протоколи.

накратко

Статията обсъжда основите на надеждното предаване на данни, прилага примери за Go, включително UDP и TCP. Базиран на път, два, три и книгите "Компютърни мрежи. Подход отгоре надолу", иначе всички обсъждат само Таненбаум и Олиферов.

Протокол на транспортния слой

Осигурява логическа връзка между процесите на приложения, работещи на различни хостове. От гледна точка на приложението, логическата връзка изглежда като канал, който директно свързва процесите.

Основи на надеждния трансфер на данни

Протоколи на транспортния слой се поддържат от крайни системи, но не и от мрежови рутери (освен - DPI). От страната на изпращача транспортният слой преобразува данните от приложния слой, които получава от процеса на изпращащото приложение, в пакети от транспортния слой, наречени сегменти.

Основи на надеждния трансфер на данни

Това става чрез разделяне (ако е необходимо) на съобщенията на приложния слой на фрагменти и добавяне на заглавка на транспортния слой към всеки от тях.

Основи на надеждния трансфер на данни

След това транспортният слой предава сегмента на мрежовия слой на подателя, където сегментът се капсулира в пакет на мрежовия слой (дейтаграма) и се изпраща. В приемащия край мрежовият слой извлича сегмента на транспортния слой от дейтаграмата и го предава на транспортния слой. След това транспортният слой обработва получения сегмент, така че неговите данни да станат достъпни за приемащото приложение.

Основи на надеждния трансфер на данни

Принципи на надеждно предаване на данни

Надеждно предаване на данни по напълно защитен канал

Най-простият случай. Изпращащата страна просто получава данните от горния слой, създава пакет, който ги съдържа, и ги изпраща към канала.

сървър

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

Надеждно предаване на данни по канал с възможни грешки

Следващата стъпка е да приемем, че всички предадени пакети са получени в реда, в който са били изпратени, но битовете в тях може да са повредени поради факта, че каналът понякога предава данни с изкривявания.

Основи на надеждния трансфер на данни

В този случай се използват следните механизми:

  • откриване на грешки;
  • обратна връзка;
  • препредаване.

Надеждни протоколи за пренос на данни, които имат подобни механизми за многократно повтаряне на предаването, се наричат ​​протоколи за автоматично повторение (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 за работа в мрежа

В хранилища.

Източник: www.habr.com

Добавяне на нов коментар