Dasar-dasar Transfer Data yang Andal

Dasar-dasar Transfer Data yang Andal

Kepada mereka yang mencari Didedikasikan untuk memahami jaringan dan protokol.

Secara singkat

Artikel ini membahas dasar-dasar transmisi data yang andal, menerapkan contoh Go, termasuk UDP dan TCP. Berdasarkan waktu, два, tiga dan buku "Jaringan Komputer. Pendekatan Top-Down", jika tidak, semua orang hanya mendiskusikan Tannenbaum dan Oliferov.

Protokol lapisan transportasi

Menyediakan koneksi logis antara proses aplikasi yang berjalan pada host berbeda. Dari perspektif aplikasi, koneksi logis terlihat seperti saluran yang menghubungkan proses secara langsung.

Dasar-dasar Transfer Data yang Andal

Protokol lapisan transport didukung oleh sistem akhir, tetapi tidak oleh router jaringan (kecuali - DPI). Di sisi pengirim, lapisan transport mengubah data lapisan aplikasi yang diterimanya dari proses pengiriman aplikasi menjadi paket lapisan transport yang disebut segmen.

Dasar-dasar Transfer Data yang Andal

Hal ini dilakukan dengan membagi (jika perlu) pesan lapisan aplikasi menjadi beberapa bagian dan menambahkan header lapisan transport ke masing-masing bagian.

Dasar-dasar Transfer Data yang Andal

Lapisan transport kemudian meneruskan segmen tersebut ke lapisan jaringan pengirim, di mana segmen tersebut dienkapsulasi dalam paket lapisan jaringan (datagram) dan dikirim. Di sisi penerima, lapisan jaringan mengekstrak segmen lapisan transport dari datagram dan meneruskannya ke lapisan transport. Selanjutnya, lapisan transport memproses segmen yang diterima sehingga datanya tersedia untuk aplikasi penerima.

Dasar-dasar Transfer Data yang Andal

Prinsip transmisi data yang andal

Transmisi data yang andal melalui saluran yang sepenuhnya aman

Kasus paling sederhana. Pihak pengirim hanya menerima data dari lapisan atas, membuat paket yang berisi data tersebut, dan mengirimkannya ke saluran.

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

Klien

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

Transmisi data yang andal melalui saluran dengan kemungkinan kesalahan

Langkah selanjutnya adalah mengasumsikan bahwa semua paket yang dikirimkan diterima sesuai urutan pengirimannya, namun bit di dalamnya mungkin rusak karena saluran terkadang mentransmisikan data dengan distorsi.

Dasar-dasar Transfer Data yang Andal

Dalam hal ini, mekanisme berikut digunakan:

  • deteksi kesalahan;
  • masukan;
  • transmisi ulang.

Protokol transfer data yang andal yang memiliki mekanisme serupa untuk mengulangi transmisi beberapa kali disebut protokol Automatic Repeat reQuest (ARQ).
Selain itu, perlu dipertimbangkan kemungkinan kesalahan penerimaan, ketika pihak penerima tidak menerima informasi apapun tentang hasil transfer paket terakhir.
Solusi untuk masalah ini, yang juga digunakan di TCP, adalah menambahkan kolom baru ke paket data yang berisi nomor urut paket.

Dasar-dasar Transfer Data yang Andal

Transmisi data yang andal melalui saluran yang tidak dapat diandalkan dapat menyebabkan distorsi dan kehilangan paket

Sayangnya, seiring dengan distorsi, ada juga kehilangan paket di jaringan.
Dan untuk mengatasi masalah ini diperlukan mekanisme:

  • menentukan fakta hilangnya paket;
  • pengiriman kembali paket yang hilang ke pihak penerima.

Selain itu, selain hilangnya paket, perlu juga diperkirakan kemungkinan hilangnya tanda terima atau, jika tidak ada yang hilang, pengirimannya dengan penundaan yang cukup lama. Dalam semua kasus, hal yang sama dilakukan: paket dikirim ulang. Untuk mengontrol waktu, mekanisme ini menggunakan penghitung waktu mundur, yang memungkinkan Anda menentukan akhir interval tunggu. Jadi di dalam paket bersih TCPKeepAlive diatur ke 15 detik secara default:

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

Pihak pengirim perlu memulai pengatur waktu setiap kali paket dikirimkan (pertama dan kedua kalinya), menangani interupsi dari pengatur waktu dan menghentikannya.

Jadi, kita telah memahami konsep utama protokol transfer data yang andal:

  • checksum;
  • nomor urut paket;
  • pengatur waktu;
  • penerimaan positif dan negatif.

Tapi itu tidak semua!

Protokol transfer data yang andal dengan pipeline

Pada varian yang telah kami pertimbangkan, protokol pengiriman yang andal sangat tidak efisien. Ini mulai “memperlambat” transmisi yang disediakan oleh saluran komunikasi seiring dengan meningkatnya RTT. Untuk meningkatkan efisiensi dan memanfaatkan kapasitas saluran komunikasi dengan lebih baik, digunakan pipeline.

Dasar-dasar Transfer Data yang Andal

Penggunaan pipeline menyebabkan:

  • meningkatkan jangkauan nomor urut, karena semua paket yang dikirim (kecuali transmisi ulang) harus diidentifikasi secara unik;
  • kebutuhan untuk meningkatkan buffer di sisi pengirim dan penerima.

Rentang nomor urut dan persyaratan ukuran buffer bergantung pada tindakan yang diambil protokol dalam menanggapi kerusakan paket, kehilangan, dan penundaan. Dalam kasus pipeline, ada dua metode untuk memperbaiki kesalahan:

  • mengembalikan N paket kembali;
  • pengulangan selektif.

Mengembalikan N paket - protokol jendela geser

Dasar-dasar Transfer Data yang Andal

Pengirim harus mendukung tiga jenis acara:

  • panggilan dengan protokol tingkat yang lebih tinggi. Ketika fungsi pengiriman data dipanggil “dari atas”, pihak pengirim terlebih dahulu memeriksa tingkat pengisian jendela (yaitu, keberadaan N pesan terkirim yang menunggu penerimaan tanda terima). Jika jendela kosong, paket baru dibuat dan dikirim, dan nilai variabel diperbarui. Jika tidak, pihak pengirim mengembalikan data ke lapisan atas, dan ini merupakan indikasi implisit bahwa jendela sudah penuh. Biasanya lapisan atas akan mencoba mengirimkan data lagi setelah beberapa waktu. Dalam aplikasi nyata, pengirim kemungkinan akan melakukan buffering data (daripada mengirimkannya segera) atau memiliki mekanisme sinkronisasi (seperti semaphore atau flag) yang memungkinkan lapisan atas memanggil fungsi kirim hanya ketika jendelanya kosong. .
  • menerima konfirmasi. Dalam protokol, untuk paket dengan nomor urut N, pengakuan umum dikeluarkan yang menunjukkan bahwa semua paket dengan nomor urut sebelum N berhasil diterima.
  • interval tunggu telah berakhir. Untuk mengetahui fakta kehilangan dan keterlambatan paket dan penerimaan, protokol menggunakan timer. Jika interval waktu habis berakhir, pihak pengirim akan mengirim ulang semua paket terkirim yang tidak diakui.

Pengulangan selektif

Ketika ukuran jendela dan produk penundaan propagasi throughput besar, sejumlah besar paket mungkin berada dalam saluran. Dalam kasus seperti itu, kesalahan paket tunggal dapat menyebabkan sejumlah besar paket dikirim ulang, yang sebagian besar tidak diperlukan.

Contoh

Puncak teoretis praktik dikumpulkan dalam implementasi praktis TCP. Dan jika seseorang tahu cara terbaik - selamat datang.

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

Klien

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

Keluaran

Mekanisme untuk memastikan transfer dan penggunaan data yang andal

Mekanisme
Aplikasi, komentar

Periksa jumlah
Digunakan untuk mendeteksi kesalahan bit dalam paket yang dikirimkan

Timer
Menghitung mundur interval batas waktu dan menunjukkan kapan interval tersebut telah habis masa berlakunya. Yang terakhir berarti bahwa dengan tingkat kemungkinan yang tinggi paket atau tanda terimanya hilang selama transmisi. Jika sebuah paket dikirimkan dengan penundaan, namun tidak hilang (kedaluwarsa prematur dari interval waktu habis), atau tanda terima hilang, transmisi ulang menyebabkan paket duplikat di sisi penerima.

Nomor seri
Digunakan untuk penomoran urut paket data yang dikirimkan dari pengirim ke penerima. Kesenjangan dalam nomor urut paket yang diterima memungkinkan penerima mendeteksi kehilangan paket. Nomor urut paket yang sama berarti bahwa paket-paket tersebut merupakan duplikat satu sama lain

Konfirmasi
Dihasilkan oleh pihak penerima dan menunjukkan kepada pihak pengirim bahwa paket atau kelompok paket terkait telah berhasil diterima. Biasanya pengakuan berisi nomor urut paket yang berhasil diterima. Tergantung pada protokolnya, konfirmasi individu dan kelompok dibedakan

Konfirmasi negatif
Digunakan oleh penerima untuk memberi tahu pengirim bahwa paket yang diterima salah. Pengakuan negatif biasanya menyertakan nomor urut paket yang tidak diterima dengan benar

Jendela, konveyorisasi
Batasi rentang nomor urut yang dapat digunakan untuk mengirimkan paket. Multicast dan jabat tangan dapat meningkatkan throughput protokol secara signifikan dibandingkan dengan menunggu pengakuan. Seperti yang akan kita lihat, ukuran jendela dapat dihitung berdasarkan kemampuan penerimaan dan buffering pihak penerima, serta tingkat beban jaringan

Lebih banyak contoh penggunaan Go untuk jaringan

В repositori.

Sumber: www.habr.com

Tambah komentar