إلى أولئك الذين
موجز
تتناول المقالة أساسيات نقل البيانات الموثوق به ، وتنفذ أمثلة على
بروتوكول طبقة النقل
يوفر اتصالاً منطقيًا بين عمليات التطبيق التي تعمل على مضيفين مختلفين. يبدو الاتصال المنطقي من وجهة نظر التطبيقات وكأنه قناة تربط العمليات مباشرة.
يتم ذلك عن طريق تقسيم (إذا لزم الأمر) رسائل طبقة التطبيق إلى أجزاء وإضافة رأس طبقة نقل لكل منها.
تقوم طبقة النقل بعد ذلك بتمرير المقطع إلى طبقة شبكة المرسل ، حيث يتم تغليف المقطع في حزمة طبقة شبكة (مخطط بيانات) وإرساله. على جانب الاستقبال ، تستخرج طبقة الشبكة مقطع طبقة النقل من مخطط البيانات وتمررها إلى طبقة النقل. بعد ذلك ، تقوم طبقة النقل بمعالجة المقطع المستلم بحيث تصبح بياناته متاحة للتطبيق المستلم.
مبادئ نقل البيانات الموثوق
نقل موثوق للبيانات عبر قناة موثوقة تمامًا
أبسط حالة. يتلقى المرسل ببساطة البيانات من الطبقة العليا ، وينشئ حزمة تحتوي عليها ، ويرسلها إلى القناة.
الخادم
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 ، هو إضافة حقل جديد إلى حزمة البيانات التي تحتوي على الرقم التسلسلي للحزمة.
نقل موثوق للبيانات عبر قناة غير موثوقة تكون عرضة للفساد وفقدان الحزم
إلى جانب التشويه ، لسوء الحظ ، هناك فقدان للحزم في الشبكة.
ولحل هذه المشكلة ، هناك آليات مطلوبة:
- تحديد حقيقة فقدان الحزمة ؛
- إعادة تسليم الحزم المفقودة للطرف المستلم.
بالإضافة إلى ذلك ، بالإضافة إلى فقد الحزمة ، من الضروري توفير إمكانية فقد الإيصال أو في حالة فقدان أي شيء ، يتم تسليمه مع تأخير كبير. في جميع الحالات ، يتم نفس الشيء: إعادة إرسال الحزمة. للتحكم في الوقت في هذه الآلية ، يتم استخدام مؤقت للعد التنازلي ، والذي يسمح لك بتحديد نهاية فترة الانتظار. حتى في الحزمة
// 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 قد تم استلامها بنجاح.
- انتهاء المهلة. لتحديد حقائق الخسائر والتأخير للحزم والإيصالات ، يستخدم البروتوكول جهاز توقيت. في حالة انتهاء المهلة الزمنية ، يقوم جانب الإرسال بإعادة إرسال جميع الحزم غير المعترف بها المرسلة.
التكرار الانتقائي
عندما يكون حجم النافذة وتأخير انتشار أوقات الصبيب كبيرًا ، يمكن أن يكون هناك عدد كبير من الحزم في خط الأنابيب. في مثل هذه الحالة ، قد يتسبب خطأ رزمة واحدة في إعادة إرسال عدد كبير من الحزم ، ومعظمها غير مطلوب.
مثال
أعلى
الخادم
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