Kepada mereka yang
Secara singkat
Artikel ini membahas dasar-dasar transmisi data yang andal, menerapkan contoh
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.
Hal ini dilakukan dengan membagi (jika perlu) pesan lapisan aplikasi menjadi beberapa bagian dan menambahkan header lapisan transport ke masing-masing bagian.
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.
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.
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.
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
// 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.
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
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
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
В
Sumber: www.habr.com