Основи надійної передачі даних

Основи надійної передачі даних

Тим, хто прагне розібратися в мережах та протоколах, присвячується.

коротко

У статті розглядаються основи надійної передачі даних, реалізуються приклади Go, у тому числі UDP та TCP. За мотивами раз, два, три і книги "Комп'ютерні мережі. Східний підхід", а то всі обговорюють лише Танненбаума та Оліферов.

Протокол транспортного рівня

Забезпечує логічну сполуку між прикладними процесами, що виконуються на різних хостах. Логічне з'єднання з погляду додатків виглядає як канал, що безпосередньо з'єднує процеси.

Основи надійної передачі даних

Протоколи транспортного рівня підтримуються кінцевими системами, але не мережними маршрутизаторами. Точок на дюйм). На стороні відправника транспортний рівень перетворює дані прикладного рівня, які отримує від передавального прикладного процесу, пакети транспортного рівня, звані сегментами.

Основи надійної передачі даних

Це робиться розбиттям (при необхідності) повідомлень прикладного рівня на фрагменти та додаванням до кожного з них заголовка транспортного рівня.

Основи надійної передачі даних

Далі транспортний рівень передає сегмент мережного рівня відправника, де сегмент інкапсулюється в пакет мережевого рівня (дейтаграму) і надсилається. На стороні, що приймає мережевий рівень витягує сегмент транспортного рівня з дейтаграми і передає його вгору транспортному рівню. Далі транспортний рівень обробляє отриманий сегмент таким чином, щоб його дані стали доступні додатку-одержувачу.

Основи надійної передачі даних

Принципи надійної передачі

Надійна передача даних абсолютно надійним каналом

Найпростіший випадок. Відправна сторона просто приймає дані від верхнього рівня, створює пакет, що їх містить, і відправляє його в канал.

Сервер

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

Додати коментар або відгук