مبانی انتقال داده های قابل اعتماد

مبانی انتقال داده های قابل اعتماد

به کسانی که به دنبال به درک شبکه ها و پروتکل ها اختصاص دارد.

خلاصه

این مقاله اصول انتقال داده قابل اعتماد را مورد بحث قرار می دهد، نمونه هایی را پیاده سازی می کند 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)
    }
}

انتقال داده قابل اعتماد از طریق یک کانال با خطاهای احتمالی

گام بعدی این است که فرض کنیم تمام بسته های ارسالی به ترتیب ارسال دریافت می شوند، اما بیت های موجود در آنها ممکن است به دلیل این واقعیت که کانال گاهی اوقات داده ها را با اعوجاج ارسال می کند خراب شود.

مبانی انتقال داده های قابل اعتماد

در این مورد، مکانیسم های زیر استفاده می شود:

  • تشخیص خطا؛
  • بازخورد؛
  • ارسال مجدد

پروتکل‌های قابل اطمینان انتقال داده که مکانیسم‌های مشابهی برای تکرار چندین بار ارسال دارند، پروتکل‌های درخواست تکرار خودکار (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)
    }
}

نتیجه

مکانیسم هایی برای اطمینان از انتقال و استفاده قابل اعتماد داده ها

سازوکار
برنامه، نظر

جمع را بررسی کنید
برای تشخیص خطاهای بیت در یک بسته ارسالی استفاده می شود

تایمر
فاصله زمانی را معکوس می شمارد و نشان می دهد که چه زمانی منقضی شده است. مورد دوم به این معنی است که با احتمال زیاد بسته یا دریافت آن در حین انتقال گم می شود. اگر بسته ای با تأخیر تحویل داده شود، اما گم نشود (انقضای زودهنگام فاصله زمانی)، یا رسید از بین برود، ارسال مجدد منجر به یک بسته تکراری در سمت گیرنده می شود.

شماره سریال
برای شماره گذاری متوالی بسته های داده ارسال شده از فرستنده به گیرنده استفاده می شود. شکاف در اعداد دنباله بسته های دریافتی به گیرنده اجازه می دهد تا از دست رفتن بسته را تشخیص دهد. همان شماره های توالی بسته ها به این معنی است که بسته ها تکراری از یکدیگر هستند

تایید
توسط گیرنده تولید می شود و به انتهای ارسال نشان می دهد که بسته یا گروهی از بسته های مربوطه با موفقیت دریافت شده است. معمولاً تصدیق شامل شماره های توالی بسته های دریافت شده با موفقیت است. بسته به پروتکل، تاییدیه های فردی و گروهی متمایز می شوند

تایید منفی
گیرنده استفاده می کند تا به فرستنده اطلاع دهد که بسته نادرست دریافت شده است. تأیید منفی معمولاً شامل شماره توالی بسته ای است که به درستی دریافت نشده است

پنجره، نوار نقاله
محدوده اعداد دنباله ای را که می توان برای انتقال بسته ها استفاده کرد، محدود کنید. Multicast و handshake می توانند به طور قابل توجهی توان عملیاتی پروتکل را در مقایسه با انتظار برای تصدیق افزایش دهند. همانطور که خواهیم دید، اندازه پنجره را می توان بر اساس قابلیت های دریافت و بافر گیرنده و همچنین سطح بار شبکه محاسبه کرد.

نمونه های بیشتری از استفاده از Go برای شبکه

В مخازن.

منبع: www.habr.com

اضافه کردن نظر