اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'شبكات حاسوبية'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 4 نتائج

  1. النمط الشائع للاتصال الذي تستخدمه برامج التطبيقات المُهيكَلة كزوج عميل / خادم client/server هو اتفاق رسائل الطلب / الرد request/reply message: يرسل العميل رسالة طلبٍ إلى الخادم، ويستجيب الخادم برسالة رد، مع تعطيل العميل (تعليق التنفيذ) انتظارًا للرد. يوضح الشكل التالي التفاعل الأساسي بين العميل والخادم في مثل هذا التبادل. إنّ بروتوكول النقل الذي يدعم نموذج الطلب / الرد أكثر بكثير من رسالة UDP تسير في اتجاه واحد متبوعةً برسالة UDP تذهب في الاتجاه الآخر، فهو يحتاج إلى التعامل مع تحديد العمليات بصورةٍ صحيحة على المضيفين البعيدين وربط الطلبات بالاستجابات. قد يحتاج أيضًا إلى التغلب على بعض أو كل قيود الشبكة الأساسية الموضحة في بيان المشكلة في بداية هذا المقال. يتغلب بروتوكول TCP على هذه القيود من خلال توفير خدمة تدفق بايتات موثوقة، ولكنه لا يتطابق تمامًا مع نموذج الطلب / الرد. يصف هذا القسم فئةً ثالثةً من بروتوكول النقل، تُسمى استدعاء الإجراء عن بُعد Remote Procedure Call أو اختصارًا RPC، والتي تتطابق بشكل وثيق مع احتياجات التطبيق المُضمَّنة في تبادل رسائل الطلب / الرد. أساسيات الإجراء عن بعد RPC بروتوكول RPC ليس بروتوكولًا تقنيًا، فمن الأفضل التفكير به كآلية عامة لبناء الأنظمة الموزعة. يُعَد RPC شائعًا لأنه يعتمد على دلالات استدعاء الإجراء المحلي، حيث يستدعي برنامج التطبيق إجراءً دون تحديد ما إذا كان محليًا أو بعيدًا ويتوقف حتى يعيد الإجراء قيمة. يمكن أن يكون مطور التطبيق غير مدرك إلى حد كبير ما إذا كان الإجراء محليًا أم بعيدًا، مما يبسط مهمته إلى حدٍ كبير. يُعرف RPC باسم استدعاء الطرائق عن بُعد remote method invocation أو اختصارًا RMI عندما تكون الإجراءات التي يتم استدعاؤها هي في الواقع طرائق methods لكائناتٍ بعيدة في لغة موجّهة بالكائنات. على الرغم من أن مفهوم RPC بسيط، إلا أن هناك مشكلتين رئيسيتين تجعله أعقد من استدعاءات الإجراءات المحلية: تملك الشبكة بين العملية المستدعية calling process والعملية المُستدعاة called process خصائصًا أعقد من اللوحة الأم المعززة backplane للحاسوب، فمن المحتمل أن تحد من أحجام الرسائل وتميل إلى فقدان الرسائل وإعادة ترتيبها على سبيل المثال. قد تحتوي الحواسيب التي تعمل عليها العمليات المستدعية والمُستدعاة على معماريات وتنسيقات تمثيل بيانات مختلفة. وبالتالي تتضمن آلية RPC الكاملة في الواقع مكونين رئيسيين: بروتوكول يدير الرسائل المرسلة بين عمليات العميل وعمليات الخادم ويتعامل مع الخصائص غير المرغوب فيها المحتملة للشبكة الأساسية. دعم لغة برمجة ومصرّف compiler لحزم الوسطاء arguments في رسالة طلب على جهاز العميل ثم ترجمة هذه الرسالة مرة أخرى إلى الوسطاء على جهاز الخادم، وبالمثل مع القيمة المُعادة. يُطلق على هذه القطعة من آلية RPC عادةً اسم مصرّف جذعي stub compiler. يوضح الشكل الآتي ما يحدث عندما يستدعي العميل إجراءً عن بُعد. أولًا، يستدعي العميل جذعًا stub محليًا للإجراء، ويمرر إليه الوسطاء التي يتطلبها هذا الإجراء. يخفي هذا الجذع حقيقة أن الإجراء بعيد عن طريق ترجمة الوسطاء إلى رسالة طلب ثم استدعاء بروتوكول RPC لإرسال رسالة الطلب إلى جهاز الخادم. يسلّم بروتوكول RPC رسالة الطلب في الخادم إلى جذع الخادم، والذي يترجمه إلى وسطاء لهذا الإجراء ثم يستدعي الإجراء المحلي. يعود إجراء الخادم بعد اكتماله في رسالة رد تُفيد بأنه يسلّم بروتوكول RPC لإرساله مرة أخرى إلى العميل. يمرر بروتوكول RPC الموجود على العميل هذه الرسالة إلى جذع العميل، والذي يترجمه إلى قيمة معادة إلى برنامج العميل. يتناول هذا القسم الجوانب المتعلقة بالبروتوكول فقط لآلية RPC، أي أنه يتجاهل الأجزاء الجذعية stubs ويركز بدلًا من ذلك على بروتوكول RPC، الذي يشار إليه أحيانًا باسم بروتوكول الطلب / الرد، الذي ينقل الرسائل بين العميل والخادم. من المهم أيضًا أن تضع في حساباتك أن برامج العميل والخادم مكتوبة في بعض لغات البرمجة، مما يعني أن آلية RPC المعينة قد تدعم Python stubs وJava stubs وGoLang stubs وما إلى ذلك، يتضمن كل منها مصطلحات خاصة باللغة عن كيفية استدعاء الإجراءات. يشير مصطلح RPC إلى نوعٍ من البروتوكولات بدلًا من معيارٍ معين مثل TCP، لذلك تختلف بروتوكولات RPC المحددة في الوظائف التي تؤديها. ولا يوجد بروتوكول RPC مهيمن واحد على عكس بروتوكول TCP، وهو بروتوكول تدفق البايتات الموثوق. وبالتالي سنتحدث في هذا القسم أكثر عن خيارات التصميم البديلة أكثر من السابق. المعرفات Identifiers في RPC يجب تأدية وظيفتين بواسطة أي بروتوكول RPC هما: وفّر مساحة اسم name space لتعريف الإجراء الذي سيُستدعى بشكل فريد. طابق كل رسالة رد برسالة الطلب المقابلة. المشكلة الأولى لها بعض أوجه التشابه مع مشكلة تحديد العقد في الشبكة، وهو شيء تفعله عناوين IP على سبيل المثال. أحد خيارات التصميم عند تحديد الأشياء هو جعل مساحة الاسم مسطحة flat أو هرمية hierarchical. تسند مساحة الاسم المسطحة ببساطة معرّفًا فريدَا غير منظم (عددًا صحيحًا مثلًا) لكل إجراء، وسيُنقَل هذا الرقم في حقلٍ واحد في رسالة طلب RPC. قد يتطلب ذلك نوعًا من التنسيق المركزي لتجنب إسناد رقم الإجراء نفسه لاثنين من الإجراءات المختلفة. ويمكن للبروتوكول تطبيق مساحة أسماء هرمية، مماثلة لتلك المستخدمة لأسماء مسار الملف، والتي تتطلب فقط أن يكون "الاسم الأساسي basename" لملفٍ ما فريدًا داخل مجلده، ومن المحتمل أن يبسط هذا النهج مهمة ضمان تفرد أسماء الإجراءات. يمكن تطبيق مساحة أسماء هرمية لآلية RPC عن طريق تحديد مجموعة من الحقول في صيغة رسالة الطلب، حقلٌ لكل مستوى من مستويات التسمية في مساحة اسماءٍ هرمية من مستويين أو ثلاثة مستويات على سبيل المثال. المفتاح لمطابقة رسالة الرد برسالة الطلب المقابلة هو تحديد أزواج الطلبات والردود بشكل فريد باستخدام حقل معرّف الرسالة. حيث يُضبَط حقل معرّف رسالة الرد على نفس قيمة رسالة الطلب. تستخدم وحدة RPC في العميل التي تتلقى الرد معرّفَ الرسالة للبحث عن الطلب المعلّق المقابل. يُوقَف المستدعي حتى استلام رسالة الرد لجعل اتفاق RPC يظهر مثل استدعاء إجراء محلي للمستدعي caller. يُحدَّد المستدعي الذي جرى وقفه عند تلقي الرد بناءً على رقم الطلب في الرد، ويتم الحصول على القيمة المعادة من الإجراء البعيد من خلال الرد reply، ويُلغى وقف المستدعي حتى يتمكن من إعادة هذه القيمة المعادة. أحد التحديات المتكررة في RPC هو التعامل مع الاستجابات غير المتوقعة، ونرى ذلك مع معرّفات الرسائل. افترض الحالة المرضيّة (ولكن الواقعية) التالية على سبيل المثال: يرسل جهاز العميل رسالة طلب بمعرف رسالة 0، ثم يتعطل ويعيد التشغيل، ثم يرسل رسالة طلب ليست ذات صلة، وأيضًا بمعرّف رسالة 0. ربما لم يكن الخادم على علم بأن العميل قد تعطل وأعيد تشغيله، عند رؤية رسالة طلب بمعرف رسالة 0، فيُقِر بها ثم يتجاهلها كونها مكررة، ولا يتلقى العميل أبدًا ردًا على الطلب. يوجد طريقةٌ واحدة للتخلص من هذه المشكلة هي استخدام معرّف إقلاع boot ID. معرف إقلاع الجهاز هو رقم يُزاد في كل مرة يعاد تشغيل الجهاز. يُقرَأ هذا الرقم من وحدة تخزين غير متطايرة (محرك أقراص أو محرك أقراص محمول)، فيُزاد ويعاد كتابته إلى جهاز التخزين أثناء إجراء بدء تشغيل الجهاز، ثم يُوضَع هذا الرقم في كل رسالة يرسلها ذلك المضيف. إذا استُلِمت رسالةٌ بمعرّف رسالة قديم ولكن مع معرف إقلاع جديد، سيجري التعرف عليها كرسالة جديدة. يتحد معرّف الرسالة ومعرّف الإقلاع لتشكيل معرّفٍ فريد لكل اتفاق. التغلب على قيود الشبكة تؤدي بروتوكولات RPC غالبًا وظائفًا إضافية للتعامل مع حقيقة أن الشبكات ليست قنوات مثالية. اثنان من هذه الوظائف هي: توفير توصيل موثوق للرسائل. دعم أحجام الرسائل الكبيرة من خلال التجزئة fragmentation وإعادة التجميع reassembly. قد يعرّف بروتوكول RPC "هذه المشكلة بعيدًا" عن طريق اختيار التشغيل فوق بروتوكول موثوق مثل بروتوكول TCP، ولكن يطبّق بروتوكول RCP في كثير من الحالات طبقة تسليم الرسائل الموثوقة الخاصة به فوق ركيزة غير موثوقة مثل UDP / IP، وبالتالي من المحتمل أن يطبّق بروتوكول RPC هذا الوثوقيةَ باستخدام الإشعارات acknowledgments والمهل الزمنية timeouts، على غرار بروتوكول TCP. الخوارزمية الأساسية واضحة ومباشرة، كما هو موضح في الجدول الزمني الوارد في الشكل التالي. حيث يرسل العميل رسالة طلب ويرسل الخادم إشعارًا بها، ثم يرسل الخادم رسالة رد ويرسل العميل إشعارًا بالرد بعد تنفيذ الإجراء. قد تضيع في الشبكة، تلك الرسالة التي تحمل بيانات (رسالة طلب أو رسالة رد) أو إشعار ACK مُرسَل للإقرار بوصول الرسالة، لذلك يحفظ كلٌّ من العميل والخادم نسخةً من كل رسالة يرسلانها حتى وصول إشعار ACK لها. يضبط كل جانب أيضًا مؤقت إعادة إرسال RETRANSMIT أي يعيد إرسال الرسالة في حالة انتهاء صلاحية هذا المؤقت، ويعيد كلا الجانبين ضبط هذا المؤقت ويحاولان مرة أخرى عددًا من المرات متفقًا عليه قبل التخلي عن الرسالة وتحريرها. إذا تلقى عميل RPC رسالةَ رد، فمن الواضح أنه يجب أن يكون الخادم قد استلم رسالة الطلب المقابلة، حيث أن رسالة الرد نفسها هي إشعارٌ ضمني implicit acknowledgment، ولن يكون أيُّ إشعارٍ إضافي من الخادم ضروريًا منطقيًا. يمكن لرسالة الطلب إرسال إشعارًا ضمنيًا لرسالة الرد السابقة على افتراض أن البروتوكول يجعل اتفاقات الطلب والرد متسلسلة، بحيث يجب إكمال اتفاقٍ واحد قبل أن يبدأ الاتفاق التالي، ولكن سيحد هذا التسلسل بشدة من أداء RPC. طريقة الخروج من هذا المأزق هي أن يطبّق بروتوكول RPC تجريد القناة channel abstraction. تكون اتفاقات الطلب / الرد متسلسلة داخل قناةٍ معينة، حيث يمكن أن يكون هناك اتفاقٌ واحد فقط نشط على قناة معينة في أي وقت معين، ولكن يمكن أن تكون هناك قنوات متعددة. يتيح تجريد القناة إمكانية تعدد اتفاقات طلب / رد RPC بين زوج العميل / الخادم. تتضمن كلُّ رسالةٍ حقلَ معرّف القناة للإشارة إلى القناة التي تنتمي إليها الرسالة. ستُقِر رسالة طلب في قناة معينة ضمنيًا بالرد السابق في تلك القناة، إذا لم يكن قد أُقِر بها بالفعل. يمكن لبرنامج التطبيق فتح قنوات متعددة إلى الخادم إذا كان يريد الحصول على أكثر من اتفاق طلب / رد بينها في نفس الوقت، ويحتاج التطبيق عندئذٍ إلى خيوطٍ threads متعددة. تُقِر رسالة الرد برسالة الطلب، ويُقِر الطلب اللاحق بالرد السابق كما هو موضح في الشكل التالي. لاحظ أننا رأينا طريقة مشابهة جدًا، تسمى القنوات المنطقية المتزامنة concurrent logical channels، في قسم سابق كطريقة لتحسين أداء آلية موثوقية التوقف والانتظار. من التعقيدات الأخرى الواجب على RPC معالجتها أن الخادم قد يستغرق وقتًا طويلًا بصورة كيفية لتحقيق النتيجة، والأسوأ من ذلك، أنه قد يتعطل قبل توليد الرد. ضع في بالك أننا نتحدث عن الفترة الزمنية بعد موافقة الخادم على الطلب ولكن قبل إرسال الرد. يمكن لعميل RPC إرسال رسالة دورية "هل أنت على قيد الحياة؟ ?Are you alive" إلى الخادم لمساعدة العميل على التمييز بين الخادم البطيء والخادم المعطَّل، ويستجيب جانب الخادم بإشعار ACK. ويمكن للخادم إرسال رسائل "ما زلت على قيد الحياة I am still alive" إلى العميل دون أن يطلبها العميل أولًا. هذا النهج أكثر قابلية للتوسع لأنه يضع المزيد من العبء (أي إدارة مؤقت المهلة الزمنية) على العملاء. قد تتضمن وثوقية RPC الخاصيةَ المعروفة باسم دلالاتٌ لمرة واحدة على الأكثر at-most-once semantics. هذا يعني أنه بالنسبة لكل رسالة طلب يرسلها العميل، تُسلَّم نسخةٌ واحدة على الأكثر من تلك الرسالة إلى الخادم. في كل مرةٍ يستدعي فيها العميلُ إجراءً بعيدًا، يُستدعى هذا الإجراء مرة واحدة على الأكثر على جهاز الخادم. نقول "مرة واحدة على الأكثر" بدلًا من "مرة واحدة تمامًا" لأنه من الممكن دائمًا فشل الشبكة أو جهاز الخادم، مما يجعل من المستحيل تسليم نسخةٍ واحدة من رسالة الطلب. يجب أن يتعّرف RPC في جانب الخادم على الطلبات المكررة ويتجاهلها لتنفيذ الدلالات مرة واحدة على الأكثر، حتى إذا كان قد استجاب بالفعل للطلب الأصلي بنجاح، ويجب أن يحتفظ ببعض معلومات الحالة التي تحدد الطلبات السابقة. تتمثل إحدى الطرق في تحديد الطلبات باستخدام الأرقام التسلسلية، لذلك يحتاج الخادم فقط إلى تذكر الرقم التسلسلي الأحدث، ولكن قد يحد ذلك من RPC وبالتالي يقتصر على طلبٍ معلق واحد إلى خادم معين في كل مرة، حيث يجب إكمال طلب واحد قبل إرسال الطلب بالرقم التسلسلي التالي. توفر القنوات الحل، فيمكن للخادم التعرّفَ على الطلبات المكررة من خلال تذكر الرقم التسلسلي الحالي لكل قناة، دون حدِّ العميل بطلب واحد في كل مرة. لا تدعم كل بروتوكولات RPC هذا السلوك، حيث يدعم البعض دلالاتٍ semantics تسمى دلالات الصفر أو أكثر zero-or-more semantics، أي أن كل استدعاء للعميل يؤدي إلى استدعاء الإجراء البعيد صفر مرةً أو أكثر. ليس من الصعب فهم كيف يمكن أن يتسبب ذلك في حدوث مشكلاتٍ لإجراءٍ بعيد قام بتغيير بعض متغيرات الحالة المحلية (زيادة عدّاد مثلًا) أو كان له بعض الآثار الجانبية المرئية خارجيًا (إطلاق صاروخ على سبيل المثال) في كل مرة يُستدعَى فيها. إذا كان الإجراء البعيد المُستدعى عديم الفعالية، سيكون للاستدعاءات المتعددة نفس تأثير استدعاءٍ واحد فقط، إذًا لا تحتاج آلية RPC إلى دعم دلالات مرة واحدة على الأكثر، ويكفي تطبيق أبسط قد يكون أسرع. يكمن السببان وراء تطبيق بروتوكول RPC تجزئة الرسالة وإعادة تجميعها كما كان الحال مع الوثوقية بعدم توفير مكدس البروتوكول الأساسي لذلك أو أنه يمكن تطبيقه بصورة أكثر كفاءة بواسطة بروتوكول RPC. ضع في اعتبارك الحالة التي يُطبَّق RPC أعلى UDP / IP ويعتمد على IP للتجزئة وإعادة التجميع. إذا فشل جزءٌ واحد فقط من الرسالة في الوصول خلال فترة زمنية معينة، فإن بروتوكول IP يتجاهل الأجزاء التي وصلت بالفعل وستضيع الرسالة فعليًا. ستنتهي مهلة بروتوكول RPC (بافتراض أنه يطبّق الوثوقية) ويعيد إرسال الرسالة. في المقابل، ضع في اعتبارك بروتوكول RPC الذي يطبّق التجزئة وإعادة التجميع الخاصة به ويرسل ACK أو NACK (إشعارًا سلبيًا) بالأجزاء الفردية. ستُكتشَف الأجزاء المفقودة ويُعاد إرسالها بسرعةٍ أكبر، وسيعاد إرسال الأجزاء المفقودة فقط، وليس الرسالة بأكملها. البروتوكولات المتزامنة مقابل البروتوكولات غير المتزامنة تتمثل إحدى طرق وصف البروتوكول في كونه متزامنًا synchronous أو غير متزامن asynchronous. يعتمد المعنى الدقيق لهذه المصطلحات على مكان استخدامها في تسلسل البروتوكول الهرمي. من الأدق في طبقة النقل التفكير فيهما على أنهما يعينان الحدود القصوى للطيف بدلًا من عدّهما بديلين متعارضين. الخاصية الرئيسية لأي نقطة على طول الطيف هي مقدار ما تعرفه عملية الإرسال بعد جواب عملية إرسال الرسالة. أي إذا افترضنا أن أحد برامج التطبيق يستدعي العملية send على بروتوكول نقل، فما الذي يعرفه التطبيق بالضبط عن نجاح العملية عند رد أو جواب العملية send؟ لا يعرف التطبيق شيئًا على الإطلاق عند عودة العملية send في الطرف غير المتزامن من الطيف، فهو لا يعرف فقط ما إذا كانت الرسالة قد استقبلها نظيرها، ولكنه لا يعرف أيضًا على وجه اليقين فيما إذا غادرت الرسالة الجهاز المحلي بنجاح أم لا. تعيد عملية send عادةً رسالةَ رد في الطرف المتزامن من الطيف، أي أن التطبيق لا يعرف فقط أن الرسالة التي أرسلها قد استلمها نظيره، ولكنه يعرف أيضًا أن النظير قد أعاد إجابة. وبالتالي تطبّق البروتوكولات المتزامنة تجريد الطلب / الرد، بينما تُستخدَم البروتوكولات غير المتزامنة إذا أراد المرسل أن يكون قادرًا على إرسال العديد من الرسائل دون الحاجة إلى انتظار الرد. تكون بروتوكولات RPC عادة بروتوكولات متزامنة باستخدام التعريف السابق. على الرغم من أننا لم نناقشها في هذا المقال، إلا أن هناك نقاطًا مثيرة للاهتمام بين هذين النقيضين. قد يطبّق بروتوكول النقل العملية send بحيث تُوقَف (أي لا تعيد قيمة) حتى تُستلَم الرسالة بنجاح على الجهاز البعيد، ولكنها تعيد قيمةً قبل أن يعالجها نظير المرسل على هذا الجهاز البعيد ويستجيب لها بالفعل. يسمى هذا أحيانًا بروتوكول مخطط بيانات موثوق reliable datagram protocol. تطبيقات وهي SunRPC وDCE وgRP لـ RPC ننتقل الآن إلى مناقشتنا لبعض أمثلة تطبيقات بروتوكولات RPC. حيث ستُبرز هذه المناقشة بعض قرارات التصميم المختلفة التي اتخذها مصممو البروتوكول. مثالنا الأول هو SunRPC، وهو بروتوكول RPC واسع الاستخدام يُعرف أيضًا باسم Open Network Computing RPC أو اختصارًا ONC RPC. المثال الثاني، الذي سنشير إليه باسم DCE-RPC، هو جزء من بيئة الحوسبة الموزعة Distributed Computing Environment أو اختصارًا DCE. بيئة DCE عبارة عن مجموعة من المعايير والبرمجيات لبناء الأنظمة الموزعة التي حددتها منظمة البرمجيات المفتوحة Open Software Foundation أو اختصارًا OSF، وهي اتحاد من شركات الحاسوب التي تضم في الأصل شركات IBM وDigital Equipment Corporation وHewlett-Packar، ويطلق على OSF اليوم اسم المجموعة المفتوحة Open Group. مثالنا الثالث هو gRPC، وهي آلية RPC شائعة جعلتها شركة Google مفتوحة المصدر، استنادًا إلى آلية RPC التي تستخدمها داخليًا لتطبيق الخدمات السحابية في مراكز البيانات الخاصة بها. تمثل هذه الأمثلة الثلاثة خيارات تصميم بديلة مثيرة للاهتمام في مساحة حلول RPC، وأقل ما قد تعتقده أنها الخيارات الوحيدة، سنشرح ثلاث آليات أخرى شبيهة بآلية RPC هي WSDL وSOAP وREST في سياق خدمات الويب لاحقًا. آلية SunRPC أصبحت آلية SunRPC معيارًا واقعيًا بفضل توزيعها الواسع مع محطات عمل Sun والدور المركزي الذي تلعبه في نظام ملفات الشبكة Network File System أو اختصارًا NFS الشهير في Sun. تبنتها منظمة IETF لاحقًا كَبروتوكول إنترنت قياسي تحت اسم ONC RPC. يمكن تطبيق آلية SunRPC عبر عدة بروتوكولات نقل مختلفة. ويوضح الشكل التالي الرسم البياني للبروتوكول عند تطبيق SunRPC على UDP. قد يستهجن أحد خبراء الطبقات الصارم فكرة تشغيل بروتوكول نقل عبر بروتوكول نقل، أو يجادل بأن RPC يجب أن يكون شيئًا آخر غير بروتوكول نقل لأنه يظهر "فوق" طبقة النقل. يُعد عمليًا قرار التصميم الخاص بتشغيل RPC على طبقة نقل حالية ذو فحوى ودلالة كبيرين، كما سيتضح في المناقشة التالية: تستخدم آلية SunRPC معرّفات من مستويين لتحديد الإجراءات البعيدة: رقم برنامج ذو 32 بت ورقم إجراء ذو 32 بت (يوجد أيضًا رقم إصدار ذو 32 بت، لكننا نتجاهل ذلك في المناقشة التالية). إذا أُسنِد رقم برنامج x00100003 على سبيل المثال لخادم NFS، ويوجد داخل هذا البرنامج الإجراء getattr هو الإجراء 1، والإجراء setattr هو الإجراء 2، والإجراء read هو الإجراء 6، والإجراء write هو الإجراء 8، وما إلى ذلك. يُرسَل رقم البرنامج ورقم الإجراء في ترويسة رسالة طلب SunRPC، والموضحة حقولها في الشكل التالي. الخادم، الذي قد يدعم عدة أرقام برامج، مسؤولٌ عن استدعاء الإجراء المحدد للبرنامج المحدد. يمثل طلب SunRPC حقًا طلبًا لاستدعاء البرنامج والإجراءات المحددة على الجهاز المعين الذي أُرسِل الطلب إليه، على الرغم من إمكانية تطبيق نفس رقم البرنامج على أجهزةٍ أخرى في نفس الشبكة. وبالتالي فإن عنوان جهاز الخادم (عنوان IP على سبيل المثال) هو طبقة ثالثة ضمنية من عنوان RPC. قد تنتمي أرقام البرامج المختلفة إلى خوادم مختلفة على نفس الجهاز. تحتوي هذه الخوادم المختلفة على مفاتيح فك تعدد إرسال demux مختلفة لطبقة النقل مثل منافذ UDP، ومعظمها أرقام غير معروفة ولكنها تُسنَد ديناميكيًا، وتسمى مفاتيح تعدد الإرسال هذه محدّدات النقل transport selectors. كيف يمكن لعميل SunRPC الذي يريد التحدث إلى برنامج معين تحديد محدّد النقل الواجب استخدامه للوصول إلى الخادم المقابل؟ الحل هو إسناد عنوانٍ معروف لبرنامجٍ واحد فقط على الجهاز البعيد والسماح لهذا البرنامج بالتعامل مع مهمة إخبار العملاء باستخدام محدّد نقل معين للوصول إلى أي برنامجٍ آخر على الجهاز. يُطلق على الإصدار الأصلي من برنامج SunRPC اسم Port Mapper، وهو يدعم فقط UDP وTCP كبروتوكولات أساسية. رقم هذا البرنامج هو x00100000، ومنفذه المعروف هو 111. يدعم برنامج RPCBIND، المُطوَّر من برنامج Port Mapper، بروتوكولات النقل الأساسية الكيفية. يستدعي كلُّ خادم SunRPC في البداية إجراءَ تسجيل RPCBIND على الجهاز الرئيسي للخادم، لتسجيل محدد النقل وأرقام البرامج التي يدعمها. يمكن للعميل البعيد استدعاء إجراء بحث RPCBIND للبحث lookup عن محدد النقل لرقم برنامجٍ معين. لجعل هذا أكثر واقعية، نفترض مثالًا باستخدام برنامج Port Mapper مع بروتوكول UDP. لإرسال رسالة طلب إلى إجراء read الخاص ببرنامج NFS، يرسل العميل أولًا رسالة طلب إلى Port Mapper على منفذ UDP المعروف 111، ويطلب العميل هذا الإجراء 3 لربط رقم البرنامج x00100003 مع منفذ UDP حيث يوجد برنامج NFS حاليًا. يرسل العميل بعد ذلك رسالة طلب SunRPC برقم البرنامج x00100003 والإجراء رقم 6 إلى منفذ UDP هذا، وتستدعي وحدة SunRPC عند ذلك المنفذ إجراء read الخاص ببرنامج NFS. يخزّن العميل أيضًا ربط رقم البرنامج إلى المنفذ تخزينًا مؤقتًا بحيث لا يحتاج للرجوع إلى Port Mapper في كل مرة يريد فيها التحدث إلى برنامج NFS. يُعَد NFS، من الناحية العملية، برنامجًا مهمًا لدرجة أنه أُعطي منفذ UDP الخاص به، ولكن نتظاهر بأن الأمر ليس كذلك لأغراض التوضيح. تشتمل ترويسات رسائل الطلب والرد على حقل XID "معرّف الاتفاق transaction ID"، كما في الشكل السابق، لمطابقة رسالة رد مع الطلب المقابل، بحيث يمكن إرجاع نتيجة استدعاء الإجراء البعيد إلى المستدعي الصحيح. XID هو معرّف الاتفاق الفريد الذي يستخدمه فقط طلبٌ واحد والرد المقابل له. لا يتذكر الخادم المعرّف XID بعد أن نجح في الرد على طلب معين. لا يستطيع SunRPC ضمان دلالات مرة واحدة على الأكثر at-most-once semantics. تعتمد تفاصيل دلالات SunRPC على بروتوكول النقل الأساسي. لا يطبق SunRPC موثوقيته الخاصة، لذلك لا يمكن الاعتماد عليه إلا إذا كان بروتوكول النقل الأساسي موثوقًا (قد يختار أي تطبيق مُشغَّل عبر SunRPC أيضًا تطبيق آليات الوثوقية الخاصة به فوق مستوى SunRPC). تعتمد القدرة على إرسال رسائل الطلب والرد التي تكون أكبر من وحدة الإرسال القصوى MTU للشبكة على النقل الأساسي. أي لا يحاول SunRPC تحسينَ النقل الأساسي عندما يتعلق الأمر بالموثوقية وحجم الرسالة. بما أن SunRPC يمكن تشغيله عبر العديد من بروتوكولات النقل المختلفة، فإن هذا يمنحه قدرًا كبيرًا من المرونة دون تعقيد تصميم بروتوكول RPC نفسه. بالعودة إلى صيغة ترويسة SunRPC بالشكل السابق، حيث تحتوي رسالة الطلب على حقول Credentials وVerifier ذات الطول المتغير، وكلاهما يستخدمه العميل بهدف استيثاق authenticate نفسه على الخادم، أي لتقديم دليل على أن العميل لديه الحق في استدعاء الخادم. تُعَد كيفية استيثاق العميل لنفسه على الخادم مشكلة عامة يجب معالجتها بواسطة أي بروتوكول يريد توفير مستوى معقول من الأمن. آلية DCE-RPC DCE-RPC هو بروتوكول RPC وهو صميم نظام DCE وكان أساس آلية RPC التي تقوم عليها Microsoft DCOM وActiveX. يمكن استخدامه مع المصرّف الجذعي stub compiler لتمثيل بيانات الشبكة Network Data Representation أو اختصارًا NDR، ولكنه يعمل أيضًا كبروتوكول RPC الأساسي لمعيار Common Object Request Broker Architecture أو اختصارًا CORBA، وهو معيار على مستوى الصناعة لبناء الأنظمة الموزعة والموجَّهة بالكائنات. يمكن تطبيق DCE-RPC، مثل SunRPC، فوق العديد من بروتوكولات النقل بما في ذلك UDP وTCP. وهو مشابه أيضًا لبروتوكول SunRPC من حيث أنه يحدد مخطط عنونة من مستويين: فك تعدد إرسال بروتوكول النقل إلى الخادم الصحيح، وإرسال DCE-RPC إلى إجراءٍ معين يصدّره هذا الخادم، ويستشير العملاء "خدمة ربط نقاط النهاية endpoint mapping service" (مماثلة لبرنامج Port Mapper الخاص ببروتوكول SunRPC) لمعرفة كيفية الوصول إلى خادمٍ معين. لكن ينفذ DCE-RPC دلالات استدعاء مرة واحدة على الأكثر at-most-once على عكس SunRPC. (يدعم DCE-RPC دلالات استدعاء متعددة، بما في ذلك الدلالات الراسخة المماثلة لدلالات SunRPC، ولكن دلالات at-most-once في معظم الأحيان هو السلوك الافتراضي). هناك بعض الاختلافات الأخرى بين المنهجين، التي سنسلط عليها الضوء في الفقرات التالية. يعطي الشكل السابق خطًا زمنيًا للتبادل النموذجي للرسائل، حيث تُسمَّى كل رسالة بنوع DCE-RPC الخاص بها. يرسل العميل رسالة طلب Request، ويرد الخادم في النهاية برسالة استجابة Response، ويرسل العميل إشعارًا Ack بالاستجابة. يرسل العميل دوريًا رسالة Ping إلى الخادم بدلًا من إقرار الخادم برسائل الطلب، والذي يستجيب برسالة Working للإشارة إلى أن الإجراء البعيد لا يزال قيد التنفيذ. إذا اُستلِم ردُّ الخادم بسرعة معقولة، فلن تُرسَل أية رسائل Ping. تُدعَم أنواع الرسائل الأخرى أيضًا على الرغم من عدم ظهورها في الشكل، حيث يمكن للعميل إرسال رسالة إنهاء Quit إلى الخادم، مطالبًا إياه بإنهاء استدعاءٍ سابق لا يزال قيد التنفيذ، فيستجيب الخادم برسالة Quack أي إشعار بالإنهاء quit acknowledgment. يمكن للخادم أيضًا الرد على رسالة طلب Request برسالة رفض Reject (تشير إلى رفض الاستدعاء)، ويمكنه الرد على رسالة Ping برسالة Nocall (تشير إلى أن الخادم لم يسمع المستدعي مطلقًا). يحدث كل اتفاق طلب / رد في DCE-RPC ضمن سياق نشاط activity. النشاط عبارة عن قناة طلب / رد منطقية بين زوج من المشاركين، ويمكن أن يكون هناك اتفاق رسالةٍ واحد فقط نشط على قناة معينة في نفس الوقت. يتعين على برامج التطبيق فتح قنوات متعددة إذا كانت تريد الحصول على أكثر من اتفاق طلب / رد فيما بينها في نفس الوقت مثل نهج القناة المنطقية المتزامنة الموصوفة أعلاه. يحدّد حقلُ ActivityId الخاص بالرسالة النشاطَ الذي تنتمي إليه الرسالة، ثم يميز حقل SequenceNum بين الاستدعاءات التي أُجريَت كجزءٍ من نفس النشاط، حيث يؤدّي هذا الحقل نفسَ الغرض من حقل XID الخاص ببروتوكول SunRPC، ولكن يتتبع DCE-RPC الرقم التسلسلي الأخير المستخدَم كجزء من نشاطٍ معين، وذلك لضمان دلالات مرة واحدة على الأكثر بخلاف SunRPC. يستخدم DCE-RPC حقل ServerBoot للاحتفاظ بمعرّف تشغيل الجهاز للتمييز بين الردود المرسلة قبل وبعد إعادة تشغيل جهاز الخادم. هناك خيار تصميم آخر أُجري في DCE-RPC مختلف عن SunRPC وهو دعم التجزئة وإعادة التجميع في بروتوكول RPC. كما هو مذكور أعلاه، حتى إذا وفّر بروتوكولٌ أساسي مثل IP التجزئة / إعادة التجميع، فإن الخوارزمية الأعقد التي تُطبَّق كجزء من RPC يمكن أن تؤدي إلى انتعاشٍ recovery أسرع وتقليلٍ في استهلاك حيز النطاق التراسلي عند فقد الأجزاء fragments. يعرّف الحقل FragmentNum بشكل فريد كل جزءٍ أو قطعة fragment تشكّل رسالة طلب أو رسالة رد معينة، ويُسنَد رقمُ جزء فريد لكل جزء DCE-RPC مثل (0 و1 و2 و3 وهكذا). يطبّق كلٌّ من العميل والخادم آلية إشعارٍ انتقائية selective acknowledgment، والتي تعمل على النحو التالي عند وصف الآلية بحيث يرسل العميل رسالة طلبٍ مجزأة إلى الخادم، وتُطبَّق نفس الآلية عندما يرسل الخادم استجابة مجزأة إلى العميل. أولًا، يحتوي كل جزءٍ fragment يشكّل رسالة طلب على FragmentNum فريد ورايةٍ flag تشير إلى ما إذا كانت هذه الرزمة packet هي جزء من استدعاء (frag) أو الجزء الأخير من استدعاء ()call، حيث تحمل رسائلُ الطلب التي تناسب رزمة واحدة رايةً. يعرف الخادم أنه تلقى رسالة الطلب كاملةً عندما يكون لديه الرزمة دون وجود فجوات في أرقام الأجزاء. ثانيًا، يرسل الخادم رسالة Fack، إشعار جزء fragment acknowledgment، إلى العميل استجابةً لكل جزءٍ قادم. يحدّد هذا الإشعار أعلى رقم للجزء الذي استلمه الخادم بنجاح، أي يكون الإشعار تراكميًا، كما هو الحال في بروتوكول TCP. ولكن يُقِر الخادم بصورة انتقائية بأي أرقام أجزاءٍ أعلى تلقاها مخالفةً للترتيب، ويفعل ذلك باستخدام متجّه بتات يحدد هذه الأجزاء المخالفة للترتيب بالنسبة لأعلى جزءٍ من الترتيب تلقاه. أخيرًا، يستجيب العميل بإعادة إرسال الأجزاء المفقودة. يوضح الشكل التالي كيفية عمل كل ذلك. لنفترض أن الخادم قد تلقى بنجاح أجزاءً fragments حتى الرقم 20، بالإضافة إلى الأجزاء 23 و25 و26. يستجيب الخادم بإشعار Fack الذي يحدد الجزء 20 على أنه أعلى جزء في الترتيب، بالإضافة إلى متجه بتات SelAck مع البت الثالث (23 = 20 + 3)، والخامس (25 = 20 + 5) ، والسادس (26 = 20 + 6) المُشغَّل. يُعطَى حجم المتجه (الذي يقاس بكلماتٍ مؤلفة من 32 بتًا) في حقل SelAckLen من أجل دعم متجه بتاتٍ طويلٍ (تقريبًا) كيفي. بما أن DCE-RPC يدعم الرسائل الكبيرة جدًا (إن طول حقل FragmentNum يبلغ 16 بتًا، مما يعني أن بإمكانه دعم 64 ألف جزء)، فليس من المناسب للبروتوكول إطلاق جميع الأجزاء التي تشكّل رسالةً بأسرع ما يمكن بما أن القيام بذلك قد يغمر جهاز الاستقبال. يطبّق DCE-RPC بدلًا من ذلك خوارزميةً للتحكم في التدفق تشبه إلى حدٍ كبير خوارزمية TCP، حيث لا تُقِر كل رسالة Fack بالأجزاء المستلَمة فحسب، بل تُعلِم المرسل أيضًا بعدد الأجزاء التي قد ترسلها الآن. هذا هو الغرض من حقل WindowSize في الشكل السابق، والذي يخدم بالضبط نفس الغرض من حقل AdvertisedWindow الخاص ببروتوكول TCP باستثناء أنه يحسب الأجزاء fragments بدلًا من البايتات. يطبّق DCE-RPC أيضًا آلية للتحكم في الازدحام مشابهة لآلية TCP. قد لا يكون من المستغرب أن تتجنب بعض بروتوكولات RPC ذلك عن طريق تجنب التجزئة نظرًا لتعقيد التحكم في الازدحام. يوجد لدى المصممين مجموعة كبيرة من الخيارات المفتوحة لهم عند تصميم بروتوكول RPC. تتخذ SunRPC نهجًا أبسط وتضيف القليل نسبيًا إلى النقل الأساسي بخلاف أساسيات تحديد الإجراء الصحيح وتحديد الرسائل. يضيف DCE-RPC المزيد من الوظائف، مع إمكانية تحسين الأداء في بعض البيئات على حساب تعقيد أكبر. آلية gRPC على الرغم من أن أصولها من Google، ولكن gRPC لا تمثل Google RPC، فيرمز الحرف "g" إلى شيء مختلف في كل إصدار. رمز الحرف "g" إلى "glamorous" في الإصدار 1.10، وأشار إلى "goose" في الإصدار 1.18.تحظى gRPC بشعبية لأنها تتيح للجميع (كمصدر مفتوح) خبرةَ عشر سنوات داخل Google من خلال استخدام RPC لإنشاء خدمات سحابية قابلة للتوسّع. هناك بعض الاختلافات الرئيسية بين gRPC والمثالين الآخرين اللذين تناولناهما للتو. الاختلاف الأكبر هو أن gRPC مصممٌ للخدمات السحابية بدلًا من نموذج العميل / الخادم الأبسط الذي سبقه، ففي عالم العميل / الخادم، يستدعي العميل طريقةً في عملية خادم معينة تعمل على جهاز خادم معين. يُفترض أن تكون إحدى عمليات الخادم كافيةً لخدمة الاستدعاءات من جميع عمليات العميل التي قد تستدعيها. يستدعي العميل باستخدام الخدمات السحابية طريقةً في خدمة service، والتي تُنفَّذ من خلال عددٍ قابلٍ للتوسّع من عمليات الخادم من أجل دعم استدعاءات عدة عملاء كيفيًا في نفس الوقت، ويعمل كلٌّ من هذه العمليات على جهاز خادم مختلف. هذا هو المكان الذي تلعب فيه السحابة: توفّر مراكز البيانات عددًا يبدو لا نهائيًا من أجهزة الخادم المتاحة لتوسيع نطاق الخدمات السحابية. نعني مصطلح "قابل للتوسع scalable" أن عدد عمليات الخادم المتطابقة التي تختار إنشائها يعتمد على ضغط العمل (أي عدد العملاء الذين يريدون الخدمة في أي وقت معين) ويمكن تعديل هذا الرقم ديناميكيًا بمرور الوقت. أحد التفاصيل الأخرى هو أن الخدمات السحابية لا تنشئ عادةً عمليةً جديدة، في حد ذاتها، ولكنها تطلق حاويةً container جديدة، وهي في الأساس عمليةٌ مغلفةٌ داخل بيئة معزولة تتضمن جميع حزم البرمجيات التي تحتاجها العملية لتعمل. تُعَد Docker اليوم المثال الأساسي لمنصة حاويات. بالعودة إلى الادعاء القائل بأن الخدمة هي أساسًا مستوىً إضافيًا من التوجّه غير المباشر فوق الخادم، وهذا يعني أن المستدعي يحدد الخدمة التي يريد استدعاءها، ويوجه موازنُ الحِمل load balancer هذا الاستدعاء إلى إحدى عمليات الخوادم العديدة المتاحة (الحاويات) التي تطبّق تلك الخدمة، كما هو موضح في الشكل السابق. يمكن تطبيق موازن الحِمل بطرقٍ مختلفة، بما في ذلك جهاز عتاد، ولكنه يُطبَّق عادةً بواسطة عملية وكيل proxy تُشغَّل في آلة افتراضية (مستضافةٌ أيضًا في سحابة) وليس كجهاز فيزيائي. هناك مجموعة من أفضل الممارسات لتطبيق شيفرة الخادم الفعلي الذي يستجيب في النهاية لهذا الطلب، وبعض الآليات السحابية الإضافية لإنشاء / إتلاف create/destroy الحاويات وطلبات موازنة الحِمل عبر تلك الحاويات. إن أفضل الممارسات في بناء الخدمات بهذه الطريقة السحابية الأصلية هي: Kubernetes وهو اليوم بمثابة المثال الأساسي لنظام إدارة الحاويات هذا، ومعمارية الخدمات الصغيرة micro-services architecture. كلا الموضوعين مثيران للاهتمام، لكنهما خارج نطاق هذه السلسلة. ما يهمنا هنا هو بروتوكول النقل في صميم gRPC. هناك خروجٌ كبير عن البروتوكولين السابقين، ليس من حيث المشاكل الأساسية التي تحتاج إلى معالجة، ولكن من حيث نهج gRPC لمعالجة هذه المشاكل. يستعين gRPC بمصادر خارجية للعديد من المشكلات التي تواجه البروتوكولات الأخرى، مما يؤدي إلى حزم gRPC لتلك القدرات في نموذجٍ سهل الاستخدام. دعنا نلقي نظرة على التفاصيل. أولًا، يعمل gRPC فوق TCP بدلًا من UDP، مما يعني أنه يستعين بمصادر خارجية لمشاكل إدارة الاتصال والنقل الموثوق لرسائل الطلب والرد ذات الحجم الكيفي. ثانيًا، يعمل gRPC فعليًا فوق نسخةٍ مؤمَّنة من بروتوكول TCP تسمى طبقة النقل الآمنة Transport Layer Security أو اختصارًا TLS، وهي طبقة رقيقة تقع فوق بروتوكول TCP في مكدس البروتوكول، مما يعني أنها تستعين بمصادر خارجية لتأمين قناة الاتصال بحيث لا يستطيع الخصوم سرقة أو التنصت على تبادل الرسائل. ثالثًا، يعمل gRPC فعليًا فوق HTTP / 2 (والذي يقع في حد ذاته فوق TCP وTLS)، مما يعني أن لدى مصادر gRPC الخارجية مشكلتان إضافيتان: (1) تشفير / ضغط البيانات الثنائية بكفاءة في رسالة، (2) تعدد إرسال عدة استدعاءات بعيدة من أجل اتصال TCP واحد. يشفّر gRPC المعرّف الخاص بالطريقة البعيدة مثل URI، ومعاملات الطلب للطريقة البعيدة كمحتوىً في رسالة HTTP، والقيمة المعادة من الطريقة البعيدة في استجابة HTTP. يوضّح الشكل التالي مكدس gRPC الكامل، والذي يتضمن أيضًا العناصر الخاصة باللغة. (تتمثل إحدى نقاط قوة gRPC في المجموعة الواسعة من لغات البرمجة التي يدعمها، ويوضح الشكل التالي مجموعة فرعية صغيرة منها فقط). نناقش TLS وHTTP في مقالات لاحقة، لكننا نجد أنفسنا في حلقة اعتمادية مثيرة للاهتمام: حيث تُعد آلية RPC تشعبًا عن بروتوكول النقل المستخدَم لتنفيذ التطبيقات الموزعة، ويُعتبر HTTP مثالًا عن بروتوكول مستوى التطبيق، إلّا أنّ gRPC يعمل فوق HTTP وليس العكس. التفسير المختصر هو أن الطبقات توفر طريقة ملائمة للبشر لفهم الأنظمة المعقدة، ولكن ما نحاول فعله حقًا هو حل مجموعة من المشكلات (مثل النقل الموثوق للرسائل ذات الحجم الكيفي وتحديد المرسلين والمستلمين، تطابق رسائل الطلب مع رسائل الرد، وما إلى ذلك) وإن الطريقة التي تجمَّع فيها هذه الحلول في البروتوكولات، ثم وضع تلك البروتوكولات فوق بعضها البعض هي نتيجةً للتغييرات المتزايدة بمرور الوقت. لو بدأ الإنترنت بآلية RPC موجودةٍ في كل مكان مثل بروتوكول TCP، فربما طُبِّق HTTP فوقها (مثل أغلب البروتوكولات الأخرى على مستوى التطبيق تقريبًا) وربما قضت Google وقتها في تحسين هذا البروتوكول بدلًا من اختراع بروتوكولٍ خاص بها (كما فعلت هي والآخرين مع بروتوكول TCP). لكن ما حدث بدلًا من ذلك أنّ الويب أصبح التطبيق القاتل للإنترنت، مما يعني أن بروتوكول التطبيق الخاص به HTTP أصبح مدعومًا عالميًا من بقية البنية التحتية للإنترنت: جدران الحماية وموازنات الحِمل والتشفير والاستيثاق والضغط وما إلى ذلك. أصبح HTTP بفعالية بروتوكولَ نقل الطلب / الرد العالمي للإنترنت، نظرًا لأن جميع عناصر الشبكة هذه قد صمِّمت للعمل جيدًا مع بروتوكول HTTP. بالعودة إلى خصائص gRPC الفريدة، فإن أكبر قيمة يقدمها هي دمج التدفق streaming في آلية RPC، أي أن gRPC يدعم أربعة أنماط طلب / رد مختلفة: RPC البسيط Simple RPC: يرسل العميل رسالة طلبٍ واحدة ويستجيب الخادم برسالة ردٍ واحدة. Server Streaming RPC: يرسل العميل رسالة طلبٍ واحدة ويستجيب الخادم بتدفقٍ من رسائل الرد. يكمل العميل عمله بمجرد حصوله على جميع استجابات الخادم. Client Streaming RPC: يرسل العميل تدفقًا من الطلبات إلى الخادم، ويرسل الخادم استجابة واحدة عادةً (ولكن ليس بالضرورة) بعد أن يتلقى جميع طلبات العميل. تدفق RPC ثنائي الاتجاه Bidirectional Streaming RPC: يبدأ العميل الاستدعاء، ولكن يمكن للعميل والخادم بعد ذلك قراءة وكتابة الطلبات والاستجابات بأي ترتيب، حيث أن التدفقات مستقلةٌ تمامًا عن بعضها. تعني هذه الحرية الإضافية في كيفية تفاعل العميل والخادم أن بروتوكول نقل gRPC يحتاج إلى إرسال بيانات وصفية metadata ورسائل تحكم إضافية، بالإضافة إلى رسائل الطلب والرد الفعلية، بين النظيرين. تتضمن الأمثلة شيفرات الخطأ Error والحالة Status (للإشارة إلى نجاح أو سبب فشل شيء ما)، وTimeouts (للإشارة إلى المدة التي يرغب العميل انتظارَ الرد فيها) ، وPING (إشعار البقاء نشطًا keep-alive للإشارة إلى أن جانبًا أو آخر لا يزال قيد التشغيل)، وEOS (إشعار نهاية التدفق end-of-stream للإشارة إلى عدم وجود المزيد من الطلبات أو الاستجابات)، وGOAWAY (إشعار من الخوادم للعملاء للإشارة إلى أنهم لن يقبلوا بعد الآن أي تدفقاتٍ جديدة). إن الطريقة التي تُمرَّر فيها معلومات التحكم هذه بين الجانبين يمليها إلى حدٍ كبير بروتوكول النقل الأساسي، الذي هو HTTP / 2 في هذه الحالة، على عكس العديد من البروتوكولات الأخرى في هذه السلسلة، حيث يتضمن HTTP بالفعل مجموعةً من حقول الترويسات وشيفرات الرد التي يستفيد منها gRPC. قد يتضمن طلب RPC البسيط (بدون تدفق) رسالة HTTP التالية من العميل إلى الخادم: HEADERS (flags = END_HEADERS) :method = POST :scheme = http :path = /google.pubsub.v2.PublisherService/CreateTopic :authority = pubsub.googleapis.com grpc-timeout = 1S content-type = application/grpc+proto grpc-encoding = gzip authorization = Bearer y235.wef315yfh138vh31hv93hv8h3v DATA (flags = END_STREAM) <Length-Prefixed Message> مما يؤدي إلى رسالة الاستجابة التالية من الخادم إلى العميل: HEADERS (flags = END_HEADERS) :status = 200 grpc-encoding = gzip content-type = application/grpc+proto DATA <Length-Prefixed Message> HEADERS (flags = END_STREAM, END_HEADERS) grpc-status = 0 # OK trace-proto-bin = jher831yy13JHy3hc تُعَد HEADERS وDATA في هذا المثال رسالتين قياسيتين للتحكم في HTTP، والتي تصف بدقة وفعالية "ترويسة الرسالة" و"حمولة الرسالة". كل سطر يتبع HEADERS (ولكن قبل DATA) هو زوج attribute = value الذي يشكّل الترويسة (فكر في كل سطر على أنه مشابه لحقل الترويسة). الأزواج التي تبدأ بنقطتين (:status = 200 على سبيل المثال) هي جزء من معيار HTTP (تشير الحالة 200 إلى النجاح مثلًا). وتلك الأزواج التي لا تبدأ بنقطتين هي تخصيصات محدّدة بالآلية gRPC (تشير grpc-encoding = gzip على سبيل المثال إلى أن البيانات في الرسالة التالية قد ضُغِطت باستخدام gzip، وتشير grpc-timeout = 1S إلى أن العميل قد ضبط مهلة زمنية مقدارها ثانية واحدة). هناك قطعة أخيرة لشرحها وهو سطر الترويسة: content-type = application/grpc+proto الذي يشير إلى أن جسم الرسالة كما حدّده سطر DATA له معنى فقط لبرنامج التطبيق، أي طريقة الخادم الذي يطلب هذا العميل الخدمة منه. تحدد سلسلة +proto أن المستلم سيكون قادرًا على تفسير البتات في الرسالة وفقًا لمواصفات واجهة مخزَن البروتوكول المؤقت Protocol Buffer أو اختصارًا proto. مخازن البروتوكول المؤقتة هي طريقة gRPC لتحديد كيفية تشفير المعاملات الممرَّرة إلى الخادم في رسالة، والتي تُستخدم بدورها لإنشاء الأجزاء الجذعية التي تقع بين آلية RPC الأساسية والوظائف الفعلية التي تُستدعى. ترجمة -وبتصرّف- للقسم Remote Procedure Call من فصل End-to-End Protocols من كتاب Computer Networks: A Systems Approach. اقرأ أيضًا المقال السابق: بروتوكولات تدفق البايتات الموثوقة في الشبكات الحاسوبية: آلية الإرسال والبدائل تثبيت وإعداد نظام الوصول عن بعد VNC على نظام توزيعة ديبيان 10 توجيه Routing الرزم ضمن الشبكات الحاسوبية
  2. بروتوكول النقل الأعقد هو البروتوكول الذي يوفر خدمة تدفق بايتات موثوقة Reliable Byte Stream وموجهة نحو الاتصال، على عكس بروتوكول فك تعدد الإرسال demultiplexing البسيط كبروتوكول UDP. أثبتت هذه الخدمة أنها مفيدة لمجموعةٍ واسعة من التطبيقات لأنها تحرر التطبيق من القلق بشأن البيانات المفقودة أو المُعاد ترتيبها. ربما يكون بروتوكول التحكم في الإرسال Transmission Control Protocol عبر الإنترنت هو البروتوكول الأكثر استخدامًا من هذا النوع، كما أنه مضبوطٌ بعناية. لهذين السببين يدرس هذا القسم بروتوكول TCP بالتفصيل، على الرغم من أننا نحدد ونناقش خيارات التصميم البديلة في نهاية القسم. يضمن بروتوكول TCP التسليمَ الموثوق به والمرتّب لتدفقٍ من البايتات. إنه بروتوكول ثنائي الاتجاه full-duplex، مما يعني أن كل اتصال TCP يدعم زوجًا من تدفقات البايتات، حيث يتدفق كلٌّ منهما في اتجاه. يتضمن أيضًا آليةً للتحكم في التدفق لكل من تدفقات البايتات هذه التي تسمح للمستقبل بتحديد مقدار البيانات التي يمكن للمرسل إرسالها في وقتٍ معين. أخيرًا، يدعم بروتوكول TCP آلية فك تعدد الإرسال، مثل بروتوكول UDP، والتي تسمح لبرامج تطبيقات متعددة على أي مضيف معين بإجراء محادثة مع نظرائها في نفس الوقت. يطبّق بروتوكول TCP أيضًا، بالإضافة إلى الميزات المذكورة أعلاه، آلية التحكم في الازدحام عالية الضبط. تتمثل فكرة هذه الآلية في التحكم في مدى سرعة إرسال بروتوكول TCP للبيانات، ليس من أجل منع المرسل من الإفراط في تشغيل جهاز الاستقبال، ولكن من أجل منع المرسل من زيادة التحميل على الشبكة. يخلط العديد من الأشخاص بين التحكم في الازدحام congestion control والتحكم في التدفق flow control، لذلك سنعيد صياغة الفرق. يتضمن التحكم في التدفق منع المرسلين من تجاوز سعة أجهزة الاستقبال، بينما يتضمّن التحكم في الازدحام منع حقن الكثير من البيانات في الشبكة، مما يتسبب في زيادة تحميل المبدّلات switches أو الروابط links. وبالتالي فإن التحكم في التدفق هو مشكلة شاملة، بينما يهتم التحكم في الازدحام بكيفية تفاعل المضيفين والشبكات. قضايا طرف إلى طرف End-to-End Issues يوجد في صميم بروتوكول TCP خوارزمية النافذة المنزلقة sliding window. على الرغم من كونها نفس الخوارزمية الأساسية المُستخدمة غالبًا على مستوى الروابط، نظرًا لأن بروتوكول TCP يعمل عبر طبقة الإنترنت بدلًا من الرابط الفيزيائي نقطة لنقطة، إلا أنّ هناك العديد من الاختلافات المهمة. يحدد هذا القسم الفرعي هذه الاختلافات ويشرح كيف تعقّد بروتوكول TCP، ثم تصف الأقسام الفرعية التالية كيف يعالج بروتوكول TCP هذه التعقيدات وغيرها. أولًا، بينما تعمل خوارزمية النافذة المنزلقة على مستوى الروابط المقدمة عبر رابطٍ فيزيائي واحد يربط دائمًا نفس جهازي الحاسوب، فيدعم بروتوكول TCP الاتصالات المنطقية بين العمليات التي تعمل على أي جهازَي حاسوب على شبكة الإنترنت. هذا يعني أن بروتوكول TCP يحتاج إلى مرحلة تأسيس اتصال صريحة يتفق خلالها طرفا الاتصال على تبادل البيانات مع بعضهما بعضًا. يماثل هذا الاختلاف الاضطرار إلى الاتصال dial up بالطرف الآخر، بدلًا من امتلاك خط هاتف مخصَّص dedicated phone line. ويحتوي بروتوكول TCP أيضًا على مرحلة تفكيك اتصال صريحة. أحد الأشياء التي تحدث أثناء إنشاء الاتصال هو قيام الطرفين بإنشاء حالة مشتركة ما لتمكين خوارزمية النافذة المنزلقة من البدء. يجب فصل الاتصال حتى يعرف كل مضيف أنه لا بأس من تحرير هذه الحالة. ثانيًا، في حين أنه يوجد لدى رابط فيزيائي واحد يربط دائمًا نفس جهازي الحاسوب وقت رحلة ذهاب وإياب round-trip time أو اختصارًا RTT ثابت، فمن المحتمل أن يكون لدى اتصالات TCP أوقات ذهابٍ وإيابٍ مختلفة. قد يساوي وقت RTT من أجل اتصال TCP بين مضيفٍ في سان فرانسيسكو ومضيفٍ في بوسطن على سبيل المثال، مفصول بينهما بعدة آلاف من الكيلومترات، 100 ميلي ثانية، بينما قد يساوي وقت RTT من أجل اتصال TCP بين مضيفين في نفس الغرفة، على بعد أمتار قليلة فقط، 1 ميلي ثانية فقط. يجب أن يكون بروتوكول TCP نفسه قادرًا على دعم كلا الاتصالين. و لجعل الأمور أسوأ، فقد يبلغ وقت RTT لِاتصال TCP بين المضيفين في سان فرانسيسكو وبوسطن 100 ميلي ثانية في الساعة 3 صباحًا، ولكنّ وقت RTT يبلغ 500 ميلي ثانية في الساعة 3 مساءً. يمكن أن تكون الاختلافات في وقت RTT ممكنةً أثناء اتصال TCP الذي لا يدوم سوى بضع دقائق. ما يعنيه هذا بالنسبة لخوارزمية النافذة المنزلقة هو أن آلية المهلة الزمنية timeout التي تؤدي إلى إعادة الإرسال يجب أن تكون قابلة للتكيُّف. (بالتأكيد، يجب أن تكون المهلة الزمنية لرابط من نقطةٍ إلى نقطة معاملًا قابلًا للإعداد، ولكن ليس من الضروري تكييف هذا المؤقت مع زوجٍ معين من العقد). ثالثًا، إمكانية إعادة ترتيب الرزم أثناء عبورها للإنترنت، إنما هذا غير ممكن على رابط من نقطة إلى نقطة حيث يجب أن تكون الرزمة الأولى الموضوعة في أحد طرفي الرابط هي أول ما يظهر في الطرف الآخر. لا تسبب الرزم المخالفة قليلًا للترتيب مشاكلًا لأن خوارزمية النافذة المنزلقة يمكنها إعادة ترتيب الرزم بصورةٍ صحيحة باستخدام الرقم التسلسلي. تكمن المشكلة الحقيقية في المدى الذي يمكن أن تصل إليه رزمٌ مخالفة للترتيب، أو، كما يُقال بطريقة أخرى، مدى تأخر وصول الرزمة إلى الوجهة. يمكن أن تتأخر الرزمة في الإنترنت في أسوأ الحالات حتى انتهاء صلاحية حقل IP الذي هو العُمر time to live أو (TTL)، وفي ذلك الوقت تُهمَل الرزمة (وبالتالي لا يوجد خطر من وصولها متأخرة). يفترض بروتوكول TCP أن كل رزمة لها عمرٌ أقصى مع العلم أن بروتوكول IP يرمي الرزم بعيدًا بعد انتهاء صلاحية حقل TTL. يُعد العمر الدقيق، المعروف باسم الحد الأقصى لعمر الجزء maximum segment lifetime أو اختصارًا MSL، خيارًا هندسيًا، والإعداد الحالي الموصى به هو 120 ثانية. ضع في بالك أن بروتوكول IP لا يفرض مباشرة هذه القيمة البالغة 120 ثانية، أي أنه ببساطة تقدير تقليدي يقوم به بروتوكول TCP لطول مدة بقاء الرزمة على الإنترنت. يجب أن يكون بروتوكول TCP جاهزًا حتى تظهر الرزم القديمة جدًا فجأة في جهاز الاستقبال، مما قد يؤدي إلى إرباك خوارزمية النافذة المنزلقة. رابعًا، تُصمَّم أجهزة الحواسيب المتصلة برابط من نقطة إلى نقطة لدعم الروابط. فإذا حُسِب جداء التأخير × حيز النطاق التراسلي للرابط ليكون 8 كيلوبايت على سبيل المثال، مما يعني أن حجم النافذة حُدِّد للسماح بما يصل إلى 8 كيلوبايت من البيانات بعدم الإقرار unacknowledged في وقت معين، وعندها فمن المحتمل أن يكون لأجهزة الحاسوب في أيٍّ من طرفي الرابط القدرة على تخزين ما يصل إلى 8 كيلوبايت من البيانات. سيكون تصميم النظام بخلاف ذلك سخيفًا. يمكن توصيل أي نوع من أجهزة الحاسوب تقريبًا بالإنترنت، مما يجعل كمية الموارد المخصصة لأي اتصال TCP متغيرًا بدرجة كبيرة، لا سيما بالنظر إلى أن أي مضيف يمكنه دعم مئات اتصالات TCP في نفس الوقت. وهذا يعني أن بروتوكول TCP يجب أن يتضمن آلية يستخدمها كل جانب "لمعرفة" الموارد (مقدار مساحة المخزن المؤقت على سبيل المثال) التي يستطيع الجانب الآخر تطبيقها على الاتصال. وهذه هي مشكلة التحكم في التدفق. خامسًا، نظرًا لأن جانب الإرسال للرابط المتصل مباشرةً لا يمكنه الإرسال بصورةٍ أسرع مما يسمح به حيز نطاق الرابط التراسلي، ولأن مضيفًا واحدًا فقط يضخ البيانات في الرابط، فلا يمكن جعل الرابط مزدحمًا عن غير قصد. يكون الحِمل على الرابط مرئيًا على شكل رتل من الرزم عند المرسل، وفي المقابل، ليس لدى جانب الإرسال من اتصال TCP فكرة عن الروابط التي سيجري اجتيازها للوصول إلى الوجهة. قد يكون جهاز الإرسال متصلًا مباشرةً بشبكة إيثرنت سريعة نسبيًا على سبيل المثال، وقادرة على إرسال البيانات بمعدل 10 جيجابت في الثانية، ولكن يجب اجتياز رابطٍ بسرعة 1.5 ميجابت في الثانية في مكان ما في منتصف الشبكة. لجعل الأمور أسوأ، قد تحاول البيانات التي تنشئها العديد من المصادر المختلفة اجتياز هذا الرابط البطيء نفسه، وهذا يؤدي إلى مشكلة ازدحام الشبكة. نختم هذه المناقشة لقضايا طرفٍ إلى طرف من خلال مقارنة نهج TCP لتوفير خدمة توصيلٍ موثوقة / مرتبة مع النهج الذي تستخدمه الشبكات القائمة على الدارات الافتراضية مثل شبكة X.25 المهمة تاريخيًا. يُفترض أن شبكة IP الأساسية غير موثوقة في بروتوكول TCP وتسلّم الرسائل مخالفةً للترتيب، أي يستخدم بروتوكول TCP خوارزمية النافذة المنزلقة على أساس طرفٍ إلى طرف لتوفير تسليمٍ موثوق / مرتب. وفي المقابل، تستخدم شبكات X.25 بروتوكول النافذة المنزلقة داخل الشبكة، على أساس النقل قفزة بقفزة hop-by-hop. الافتراض الكامن وراء هذا النهج هو أنه إذا ُسلِّمت الرسائل بصورة موثوقة ومرتّبة بين كل زوج من العقد على طول المسار بين مضيف المصدر والمضيف الوجهة، فإن الخدمة من طرفٍ إلى طرف تضمن أيضًا تسليمًا موثوقًا / مرتبًا. تكمن مشكلة هذا النهج الأخير في أن سلسلةً من الضمانات السريعة المتتالية من نقل قفزة بقفزة لا تضيف بالضرورة ضمانًا شاملًا. أولًا، إذا أضيف رابطٌ غير متجانس (رابط إيثرنت مثلًا) إلى أحد طرفي المسار، فلا يوجد ضمان بأن هذه الخطوة ستحافظ على نفس الخدمة مثل الخطوات الأخرى. ثانيًا، إذا ضمِن بروتوكول النافذة المنزلقة تسليم الرسائل بصورة صحيحة من العقدة A إلى العقدة B، ومن ثم من العقدة B إلى العقدة C، فإنه لا يضمن أن العقدة B تتصرف بصفةٍ مثالية، حيث عُرفت عقد الشبكة بإدخال أخطاءٍ في الرسائل أثناء نقلها من مخزن الإدخال المؤقت إلى مخزن الإخراج المؤقت. ومن المعروف أيضًا أن عقد الشبكة تعيد ترتيب الرسائل عن طريق الخطأ. لا يزال من الضروري توفير فحوصات طرفٍ إلى طرف لضمان خدمة موثوقة / مرتبة نتيجة لأسباب الضعف البسيطة هذه، على الرغم من تطبيق المستويات الأدنى من النظام أيضًا لهذه الوظيفة. صيغة الجزء segment format بروتوكول TCP هو بروتوكول موجَّهٌ بالبايت، مما يعني أن المرسل يكتب البايت في اتصال TCP ويقرأ المتلقي البايت من خلال اتصال TCP. على الرغم من أن "تدفق البايتات" يصف الخدمة التي يقدمها بروتوكول TCP لعمليات التطبيق، فإن بروتوكول TCP نفسه لا يرسل البايتات الفردية عبر الإنترنت. وبدلًا من ذلك، يخزن بروتوكول TCP على المضيف المصدر ما يكفي من البايتات من عملية الإرسال لملء رزمةٍ بحجم معقول ثم يرسل هذه الرزمة إلى نظيره على المضيف الوجهة. يفرغ بروتوكول TCP على المضيف الوجهة بعد ذلك محتويات الرزمة في مخزن الاستلام المؤقت، وتقرأ عملية الاستلام من هذا المخزن المؤقت في أوقات فراغها. يوضح الشكل التالي هذا الموقف، حيث يُظهر تدفق البيانات في اتجاه واحد فقط بهدف التبسيط. تذكر أن اتصال TCP واحدًا يدعم عمومًا تدفقات البايتات في كلا الاتجاهين. تسمَّى الرزم المتبادلة بين نظراء بروتوكول TCP في الشكل السابق باسم الأجزاء segments، لأن كل واحد يحمل جزء من تدفق البايتات. يحتوي كل جزء TCP على العنوان الموضح في الشكل التالي. وستوضّح معظم هذه الحقول ضمن هذا القسم. يحدد حقلا SrcPort وDstPort منفذي ports المصدر والوجهة، على التوالي، تمامًا كما في بروتوكول UDP. يتحد هذان الحقلان، بالإضافة إلى عناوين IP المصدر والوجهة، لتعريف كل اتصال TCP بشكل فريد. وهذا يعني أن مفتاح فك تعدد الإرسال demux الخاص ببروتوكول TCP مُعطى بواسطة صف رباعي العناصر 4-tuple (حيث tuple هو (صف وتُجمَع إلى صفوف) هي بنية بيانات تُمثِّل سلسلةً مرتبة من العناصر غير القابلة للتبديل، وبالتالي لا يمكن تعديل القيم الموجودة فيها) وهنا هذا الصف مؤلَّف من 4 عناصر. (SrcPort, SrcIPAddr, DstPort, DstIPAddr) لاحظ أن اتصالات TCP تأتي وتذهب، وبالتالي من الممكن إنشاء اتصال بين زوجٍ معين من المنافذ، واستخدامه لإرسال البيانات واستلامها، وإغلاقه، ثم تضمين نفس زوج المنافذ في اتصال ثانٍ في وقت لاحق. نشير أحيانًا إلى هذه الحالة على أنهما تجسيدان incarnations مختلفان لنفس الاتصال. تُضمَّن جميع حقول Acknowledgement و SequenceNum و AdvertisedWindow في خوارزمية نافذة بروتوكول TCP المنزلقة. بما أن بروتوكول TCP هو بروتوكول موجهٌ بالبايت، فإن لكل بايت من البيانات رقم تسلسلي. يحتوي حقل SequenceNum على رقمٍ تسلسلي للبايت الأول من البيانات المنقولة في هذا الجزء، ويحمل حقلا Acknowledgement و AdvertisedWindow معلومات حول تدفق البيانات في الاتجاه الآخر. لتبسيط مناقشتنا، نتجاهل حقيقة أن البيانات يمكن أن تتدفق في كلا الاتجاهين، ونركز على البيانات التي لها رقم تسلسلي SequenceNum معين يتدفق في اتجاه واحد، وتتدفق قيم الحقلين Acknowledgement و AdvertisedWindow في الاتجاه المعاكس، كما هو موضح في الشكل التالي: يُستخدم حقل الرايات Flags المكوّن من 6 بتات لنقل معلومات التحكم بين نظراء بروتوكول TCP. تتضمن الرايات المحتملة رايات SYN و FIN و RESET و PUSH و URG و ACK. تُستخدَم رايتا SYN و FIN عند إنشاء اتصال TCP وإنهائه على التوالي. تُضبَط الراية ACK في أي وقت يكون حقل Acknowledgement صالحًا، مما يعني أنه يجب على المستلم الانتباه إليه. تشير الراية URG إلى أن هذا الجزء يحتوي على بياناتٍ عاجلة، فعند تعيين هذه الراية، يشير حقل UrgPtr إلى مكان بدء البيانات غير العاجلة الموجودة في هذا الجزء. يجري احتواء البيانات العاجلة في الجزء الأمامي من جسم الجزء segment body، بعدد بايتاتٍ يصل إلى قيمة الراية UrgPtr في الجزء. تشير الراية PUSH إلى أن المرسل قد استدعى عملية PUSH، مما يشير إلى الجانب المستلم من TCP بوجوب إعلام عملية الاستلام بهذه الحقيقة. أخيرًا، تشير الراية RESET إلى أن جهاز الاستقبال قد أصبح مرتبكًا، لأنه تلقى جزءًا لم يتوقع تلقيه على سبيل المثال، وبالتالي يريد إلغاء الاتصال. أخيرًا، يُستخدَم حقل المجموع الاختباري Checksum تمامًا بنفس الطريقة المستخدمة في بروتوكول UDP، أي يُحسَب عبر ترويسة TCP وبيانات TCP والترويسة الزائفة pseudoheader، والتي تتكون من عنوان المصدر وعنوان الوجهة وحقول الطول من ترويسة IP. المجموع الاختباري مطلوب لبروتوكول TCP في كلٍ من IPv4 و IPv6. بما أن ترويسة TCP متغيرة الطول (يمكن إرفاق الخيارات بعد الحقول الإلزامية)، فيُضمَّن حقل HdrLen الذي يعطي طول الترويسة بكلمات ذات حجم 32 بتًا. يُعرف هذا الحقل أيضًا باسم حقل الإزاحة Offset، لأنه يقيس الإزاحة من بداية الرزمة إلى بداية البيانات. تأسيس الاتصال وإنهاؤه يبدأ اتصال TCP عندما يقوم عميلٌ "مستدعٍ caller" بفتحٍ فعّال active open لخادم "مُستدعَى callee". وبفرض أن الخادم قد أجرى في وقتٍ سابق فتحًا سلبيًا غير فعّال passive open، فيشارك الجانبان في تبادل الرسائل لإنشاء الاتصال. (تذكر أن الطرف الذي يرغب في بدء اتصال يجري فتحًا فعالًا، بينما الطرف الذي يرغب في قبول الاتصال يجري فتحًا سلبيًا. ويسمح بروتوكول TCP بإعداد الاتصال ليكون متماثلًا symmetric، حيث يحاول كلا الجانبين فتح الاتصال في نفس الوقت، ولكن الحالة الشائعة هي أن يجري أحد الجانبين فتحًا فعّالًا والآخر يجري فتحًا سلبيًا. يبدأ الطرفان في إرسال البيانات بعد انتهاء مرحلة إنشاء الاتصال فقط. وبالمثل، فبمجرد انتهاء المشارك من إرسال البيانات، فإنه يغلق اتجاهًا واحدًا للاتصال، مما يتسبب في بدء بروتوكول TCP بجولة من رسائل إنهاء الاتصال. لاحظ أنه في حين أن إعداد الاتصال هو نشاط غير متماثل asymmetric (أحد الجانبين يجري فتحًا سلبيًا والجانب الآخر يجري فتحًا فعّالًا)، فإن تفكيك teardown الاتصال يكون متماثلًا symmetric (يجب على كل جانب إغلاق الاتصال بشكل مستقل). لذلك من الممكن أن يكون أحد الطرفين قد أنهى إغلاقًا، مما يعني أنه لم يعد بإمكانه إرسال البيانات، ولكن يجب إبقاء النصف الآخر من الاتصال ثنائي الاتجاه مفتوحًا ومواصلة إرسال البيانات بالنسبة للجانب الآخر. طريقة المصافحة الثلاثية Three-Way Handshake تسمى الخوارزمية التي يستخدمها بروتوكول TCP لإنشاء اتصال وإنهائه بمصافحة ثلاثية. نصف أولًا الخوارزمية الأساسية ثم نوضّح كيف يستخدمها بروتوكول TCP. تتضمن طريقة المصافحة الثلاثية تبادل ثلاث رسائل بين العميل والخادم، كما هو موضح في الجدول الزمني الوارد في الشكل التالي. الفكرة هي وجود طرفين يريدان الاتفاق على مجموعة من المعاملات، والتي هي أرقام البداية التسلسلية التي يخطط الجانبان لاستخدامها في تدفقات البايتات الخاصة بهما في حالة فتح اتصال TCP. قد تكون هذه المعاملات أي حقائق يريد كل جانب أن يعرفها الجانب الآخر. أولًا، يرسل العميل (المشارك الفعّال) جزءًا إلى الخادم (المشارك السلبي) يوضح الرقم التسلسلي الأولي الذي يخطط لاستخدامه (Flags=SYN,SequenceNum= x). يستجيب الخادم بعد ذلك بجزء واحد يقوم بوظيفة مزدوجة وهي الإقرار برقم العميل التسلسلي Flags = ACK وAck = x + 1، وتحديد رقم البداية التسلسلي الخاص به Flags = SYN وSequenceNum = y. وبالتالي ضُبطت بتات SYN وACK في حقل Flags في هذه الرسالة الثانية. أخيرًا، يستجيب العميل بجزء ثالث يعترف برقم الخادم التسلسلي Flags = ACK وAck = y + 1. السبب الذي يجعل كل جانب يعترف برقمٍ تسلسلي أكبر بمقدار واحد عن الرقم المرسَل هو أن حقل الإشعار Acknowledgement يحدد بالفعل "الرقم التسلسلي التالي المتوقع"، وبالتالي يُعترف ضمنيًا بجميع الأرقام التسلسلية السابقة. جرى جدولة مؤقتٍ timer لكل جزء من الجزأين الأولين على الرغم من عدم ظهوره في هذا المخطط الزمني. وعند عدم تلقي الاستجابة المتوقعة، فسيُعاد إرسال الجزء. قد تسأل نفسك لماذا يجب على العميل والخادم تبادل أرقام البداية التسلسلية مع بعضهما بعضًا في وقت إعداد الاتصال. سيكون من الأسهل إذا بدأ كل جانب ببساطة برقمٍ تسلسلي "معروف"، مثل 0. تتطلب مواصفات TCP أن يختار كل جانب من جوانب الاتصال رقم بداية تسلسليًا أوليًا عشوائيًا، والسبب في ذلك هو الحماية من وجود تجسيدين لنفس الاتصال يعيدان استخدام نفس الأرقام التسلسلية في وقتٍ قريب جدًا، أي في الوقت الذي لا يزال فيه فرصةٌ بأن يتداخل جزءٌ من تجسيد اتصالٍ سابق مع تجسيد اتصالٍ لاحق. مخطط انتقال الحالة State-Transition Diagram بروتوكول TCP معقدٌ بدرجةٍ كافية بحيث تتضمن مواصفاته مخطط انتقال الحالة. توجد نسخةٌ من هذا المخطط في الشكل الآتي. يوضح هذا المخطط فقط الحالات المشاركة في فتح اتصال (كل شيء فوق الحالة ESTABLISHED) وفي إغلاق الاتصال (كل شيء أدنى الحالة ESTABLISHED). يكون كل شيء يحدث أثناء فتح الاتصال، أي تشغيل خوارزمية النافذة المنزلقة، مخفيًا في الحالة ESTABLISHED. من السهل فهم مخطط انتقال حالة TCP، حيث يشير كل صندوقٍ إلى حالة يمكن لأحد طرفي اتصال TCP أن يجد نفسه فيها. تبدأ جميع الاتصالات في الحالة CLOSED، ومع تقدم الاتصال، ينتقل الاتصال من حالة إلى أخرى وفقًا للأقواس. يُسمَّى كل قوس بوسم tag لنموذج حدث / إجراء event/action. وبالتالي إذا كان الاتصال في حالة LISTEN مع وصول جزء SYN (أي جزء مع ضبط راية SYN على سبيل المثال)، ينتقل الاتصال إلى الحالة SYN_RCVD ويتخذ إجراء الرد بجزء ACK + SYN. لاحظ أن نوعين من الأحداث يؤديان إلى انتقال الحالة: وصول جزء من النظير (الحدث على القوس من LISTEN إلى SYNRCVD على سبيل المثال) استدعاء عملية التطبيق المحلي عمليةً على بروتوكول TCP (حدث الفتح الفعال على القوس من الحالة CLOSED إلى SYNSENT على سبيل المثال). بعبارة أخرى، يحدّد مخطط انتقال الحالة الخاص ببروتوكول TCP بفعالية دلالات semantics كلٍ من واجهة نظير لنظير peer-to-peer interface وَواجهة خدمته. تُوفَّر صيغة syntax لهاتين الواجهتين من خلال صيغة الجزء ومن خلال بعض واجهات برمجة التطبيقات على التوالي، مثل واجهة برمجة تطبيقات المقبس socket API. دعنا الآن نتتبع التحولات النموذجية المأخوذة من خلال المخطط في الشكل السابق. ضع في اعتبارك أنه في كل طرفٍ من طرفي الاتصال، يجري بروتوكول TCP انتقالات مختلفة من حالة إلى أخرى. يستدعي الخادم أولًا عملية فتحٍ سلبية على بروتوكول TCP عند فتح اتصال، مما يؤدي إلى انتقال TCP إلى حالة LISTEN. يجري العميل فتحًا فعالًا في وقت لاحق، مما يؤدي إلى إرسال نهاية الاتصال جزء SYN إلى الخادم والانتقال إلى حالة SYNSENT. ينتقل الخادم إلى حالة SYNRCVD عندما يصل جزء SYN إليه ويستجيب بجزء SYN + ACK. يؤدي وصول هذا الجزء إلى انتقال العميل إلى الحالة ESTABLISHED وإرسال ACK مرة أخرى إلى الخادم. وينتقل الخادم أخيرًا إلى الحالة ESTABLISHED عند وصول ACK. وبالتالي كأننا قد قمنا للتو بتتبع طريقة المصافحة الثلاثية. هناك ثلاثة أشياء يجب ملاحظتها حول النصف المخصص لإنشاء الاتصال في مخطط انتقال الحالة. أولًا، في حالة فقد ACK الخاص بالعميل إلى الخادم، وهو ما يتوافق مع المرحلة الثالثة من طريقة المصافحة الثلاثية، فإن الاتصال لا يزال يعمل بصورةٍ صحيحة. وذلك لأن جانب العميل موجود بالفعل في حالة ESTABLISHED، لذلك يمكن أن تبدأ عملية التطبيق المحلي في إرسال البيانات إلى الطرف الآخر. سيكون لكل جزء من أجزاء البيانات هذه مجموعة رايات ACK، والقيمة الصحيحة في حقل Acknowledgement، لذلك سينتقل الخادم إلى الحالة ESTABLISHED عند وصول جزء البيانات الأول. هذه في الواقع نقطةٌ مهمة حول بروتوكول TCP، حيث يبلّغ كل جزء عن الرقم التسلسلي الذي يتوقع المرسل رؤيته بعد ذلك، حتى إذا كان هذا يكرّر نفس الرقم التسلسلي الموجود في جزءٍ واحد أو أكثر من الأجزاء السابقة. الشيء الثاني الواجب ملاحظته حول مخطط انتقال الحالة هو أن هناك انتقالًا مضحكًا خارج حالة LISTEN عندما تستدعي العملية المحلية عملية إرسال على بروتوكول TCP. بمعنى أنه من الممكن للمشارك السلبي تحديد طرفي الاتصال (أي نفسه والمشارك البعيد الذي يرغب في الاتصال به)، ومن ثم تغيير رأيه بشأن انتظار الجانب الآخر وإنشاء اتصال فعال بدلًا من ذلك. على حد علمنا، هذه ميزة من ميزات بروتوكول TCP التي لا تستفيد منها أية عملية تطبيق. آخر شيء يجب ملاحظته بشأن المخطط هو الأقواس غير الظاهرة. تجدول معظم الحالات التي تتضمن إرسال جزءٍ إلى الجانب الآخر أيضًا مهلةً زمنية timeout تؤدي في النهاية إلى إعادة إرسال الجزء عند عدم حدوث الاستجابة المتوقعة. لا تُوصف عمليات إعادة الإرسال هذه في مخطط انتقال الحالة. في حال عدم وصول الاستجابة المتوقعة بعد عدة محاولات، يستسلم بروتوكول TCP ويعود إلى الحالة CLOSED. الشيء المهم حول عملية إنهاء الاتصال الواجب أخذه بعين الاعتبار هو أن عملية التطبيق على جانبي الاتصال يجب أن تغلق نصف الاتصال الخاص بها بشكل مستقل. إذا أغلق جانبٌ واحد فقط الاتصال، فهذا يعني أنه ليس لديه المزيد من البيانات للإرسال، ولكنه لا يزال متاحًا لتلقي البيانات من الجانب الآخر. يؤدي هذا إلى تعقيد مخطط انتقال الحالة لأنه يجب أن يأخذ في الاعتبار احتمال أن يستدعي الجانبان معامل الإغلاق في نفس الوقت، بالإضافة إلى احتمال أن يستدعي أحد الجانبين الإغلاق ثم، في وقت لاحق، يستدعي الجانب الآخر الإغلاق. وبالتالي يوجد في أي جانبٍ ثلاثُ مجموعات من الانتقالات التي تحصل على اتصال من الحالة ESTABLISHED إلى الحالة CLOSED: يجري إغلاق هذا الجانب أولًا: ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED يغلق الجانب الآخر أولًا: ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED يغلق كلا الجانبين في نفس الوقت: ESTABLISHED → FIN_WAIT_1 → CLOSING → TIME_WAIT → CLOSED يوجد في الواقع تسلسل رابع، وإن كان نادرًا، للانتقالات التي تؤدي إلى الحالة CLOSED، حيث يتبع القوس من الحالة FINWAIT1 إلى الحالة TIME_WAIT. الشيء الرئيسي الواجب معرفته حول تفكيك الاتصال هو أن الاتصال في حالة TIME_WAIT لا يمكنه الانتقال إلى حالة CLOSED حتى ينتظر ضِعف الحد الأقصى من الوقت الذي قد يبقى فيه مخطط بيانات IP على الإنترنت (أي 120 ثانية). والسبب في ذلك هو أنه بينما يرسل الجانب المحلي من الاتصال ACK كاستجابةٍ للجزء FIN الخاص بالجانب الآخر، فإنه لا يعرف أن ACK قد سُلِّم بنجاح. لذلك قد يعيد الجانب الآخر إرسال الجزء FIN الخاص به، وقد يتأخر الجزء FIN الثاني في الشبكة. إذا سُمِح للاتصال بالانتقال مباشرةً إلى الحالة CLOSED، فقد يأتي زوجٌ آخر من عمليات التطبيق ويفتح نفس الاتصال (مثل استخدم نفس زوج أرقام المنافذ)، وقد يبدأ الجزء FIN المتأخر من تجسيد الاتصال السابق على الفور في إنهاء التجسيد اللاحق لذلك الاتصال. إعادة النظر في خوارزمية النافذة المنزلقة Sliding Window Revisited نحن الآن جاهزون لمناقشة بروتوكول TCP لخوارزمية النافذة المنزلقة، والتي تخدم عدة أغراض: (1) تضمن التسليم الموثوق للبيانات، (2) تضمن تسليم البيانات بالترتيب، (3) تفرض التحكم في التدفق بين المرسل والمُستقبِل. استخدام بروتوكول TCP لخوارزمية النافذة المنزلقة هو نفسه على مستوى الرابط بالنسبة لأول وظيفتين من هذه الوظائف الثلاث، بينما يختلف بروتوكول TCP عن خوارزمية مستوى الرابط في أنه يحتوي على وظيفة التحكم في التدفق أيضًا. يعلن جهاز الاستقبال عن حجم النافذة للمرسل بدلًا من وجود نافذةٍ منزلقة ذات حجم ثابت، ويحدث ذلك باستخدام حقل AdvertisedWindow في ترويسة TCP، ويمتلك المرسل بعد ذلك على ما لا يزيد عن قيمة بايتات الحقل AdvertisedWindow من البيانات غير المعترف بها في أي وقت. يحدد جهاز الاستقبال قيمة مناسبة للحقل AdvertisedWindow بناءً على مقدار ذاكرة الاتصال المخصصة لغرض تخزين البيانات مؤقتًا. الفكرة هي منع المرسل من الإفراط في تشغيل مخزن المستقبل المؤقت (سنناقش هذا بإسهاب أدناه). التسليم الموثوق والمرتب افترض الموقف الموضح في الشكل التالي لمعرفة كيفية تفاعل جانبي الإرسال والاستقبال من بروتوكول TCP مع بعضهما بعضًا لتنفيذ تسليمٍ موثوقٍ ومرتّب. يحتفظ TCP على جانب الإرسال بمخزن إرسال مؤقت، ويُستخدم هذا المخزن المؤقت لتخزين البيانات التي أُرسلت ولكن لم يجري الاقرار بها بعد، وكذلك البيانات التي كتبها التطبيق المرسل ولكن لم تُرسل. يحتفظ TCP على جانب الاستقبال بمخزن استقبالٍ مؤقت، حيث يحتفظ هذا المخزن المؤقت بالبيانات التي تصل مخالفةً للترتيب، وكذلك البيانات ذات الترتيب الصحيح (مثل عدم وجود بايتاتٍ مفقودة في وقت سابق من التدفق) ولكن عملية التطبيق لم تتح لها الفرصة لقراءتها بعد. نتجاهل في البداية حقيقة أن كلًا من المخازن المؤقتة والأرقام التسلسلية ذات حجم محدود وبالتالي ستلتف في النهاية لجعل المناقشة التالية أسهل، ولا نفرق أيضًا بين المؤشر إلى المخزن المؤقت حيث يُخزَّن بايتٌ معين من البيانات وبين الرقم التسلسلي لذلك البايت. بالنظر أولًا إلى جانب الإرسال، يحتفظ مخزن الإرسال المؤقت بثلاث مؤشرات، لكل منها معنى واضح: LastByteAcked وLastByteSent وLastByteWritten. أي: LastByteAcked <= LastByteSent بما أن المستقبل لا يمكنه أن يقِر أو يرسل إشعارًا بوصول بايتٍ لم يُرسَل بعد، ولأن بروتوكول TCP لا يمكنه إرسال بايتٍ لم تكتبه عملية التطبيق بعد، فلاحظ أيضًا أنه ليس ضروريًا حفظ أي من البايتات الموجودة على يسار LastByteAcked في المخزن المؤقت لأنه جرى التعرف عليها بالفعل، ولا يلزم تخزين أي من البايتات الموجودة على يمين LastByteWritten لأنها لم تُنشَأ بعد. LastByteSent <= LastByteWritten يجري الاحتفاظ بمجموعة مماثلة من المؤشرات (الأرقام التسلسلية) على جانب الاستقبال: LastByteRead وNextByteExpected وLastByteRcvd، ولكن التفاوتات أقل بسبب مشكلة التسليم المخالف للترتيب. تكون العلاقة الأولى LastByteRead <NextByteExpected صحيحة لأنه لا يمكن للتطبيق قراءة البايت حتى يُستلَم ويجب استقبال جميع البايتات السابقة أيضًا. يشير NextByteExpected إلى البايت مباشرةً بعد أحدث بايت لتلبية هذا المعيار. وثانيًا عندها إذا وصلت البيانات بالترتيب، فإن NextByteExpected يشير إلى البايت بعد LastByteRcvd، بينما إذا وصلت البيانات مخالفةً للترتيب، فإن NextByteExpected يشير إلى بداية الفجوة gap الأولى في البيانات، كما في الشكل السابق. لاحظ أنه لا تحتاج البايتات الموجودة على يسار LastByteRead أن تُخزَّن مؤقتًا لأن عملية التطبيق المحلي قد قرأتها بالفعل، ولا تحتاج البايتات الموجودة على يمين LastByteRcvd إلى تخزينها مؤقتًا لأنها لم تصل بعد. NextByteExpected <= LastByteRcvd + 1 التحكم في التدفق Flow Control معظم المناقشة أعلاه مشابهة لتلك الموجودة في خوارزمية النافذة المنزلقة القياسية. الاختلاف الحقيقي الوحيد هو أننا هذه المرة أوضحنا حقيقة أن عمليات تطبيق المرسل والمستقبل تملأ وتفرّغ المخزن المؤقت المحلي، على التوالي. (أخفت المناقشة السابقة حقيقة أن البيانات الواردة من عقدة المنبع تملأ مخزن الإرسال المؤقت وأن البيانات التي تُرسَل إلى عقدة المصب تفرّغ مخزن الاستقبال المؤقت). لابد من الفهم الجيد لذلك قبل المتابعة، إذ تأتي الآن النقطة التي تختلف فيها الخوارزميتان بصورةٍ أكبر. نعيد في ما يلي تقديم حقيقة أن كلا المخزنين المؤقتين لهما حجمٌ محدد، يُشار إليهما MaxSendBuffer وMaxRcvBuffer، على الرغم من أننا لا نقلق بشأن تفاصيل كيفية تطبيقها. أي نحن مهتمون فقط بعدد البايتات التي تُخزَّن مؤقتًا وليس في المكان الذي تُخزَّن فيه هذه البايتات بالفعل. تذكر أنه في بروتوكول النافذة المنزلقة، يحدّد حجمُ النافذة مقدارَ البيانات التي يمكن إرسالها دون انتظار إشعارٍ من المستقبل. وبالتالي يخنق جهاز الاستقبال المرسل عن طريق الإعلان عن نافذة لا تزيد عن كمية البيانات التي يمكنه تخزينها مؤقتًا. لاحظ أن بروتوكول TCP على جانب الاستقبال يجب أن يحافظ على ما يلي: LastByteRcvd - LastByteRead <= MaxRcvBuffer وذلك لتجنب تجاوز المخزن المؤقت الخاص به، لذلك يعلن عن نافذة بحجم AdvertisedWindow = MaxRcvBuffer - ((NextByteExpected - 1) - LastByteRead) والذي يمثل مقدار المساحة الخالية المتبقية في المخزن المؤقت الخاص به. يُقِر المستلم بالبيانات بمجرد وصولها طالما وصلت جميع البايتات السابقة أيضًا. وينتقل LastByteRcvd إلى اليمين (أي يزداد)، مما يعني أن النافذة المعلن عنها قد تتقلص، حيث يعتمد ما إذا تقلّصت أم لا على مدى سرعة عملية التطبيق المحلي في استهلاك البيانات. إذا كانت العملية المحلية تقرأ البيانات بنفس السرعة التي تصل بها، مما يؤدي إلى زيادة LastByteRead بنفس معدل LastByteRcvd، فإن النافذة المُعلن عنها تظل مفتوحة، أي AdvertisedWindow = MaxRcvBuffer، ولكن إذا تأخرت عملية الاستلام، ربما لأنها تؤدي عمليةً مكلفة للغاية على كل بايت من البيانات التي تقرأها، فإن النافذة المُعلن عنها تصبح أصغر مع كل جزء يصل حتى تصل في النهاية إلى القيمة 0. يجب أن يلتزم بروتوكول TCP على جانب الإرسال بالنافذة المعلن عنها من جهاز الاستقبال. هذا يعني أنه في أي وقت، يجب أن يضمن أن: LastByteSent - LastByteAcked <= AdvertisedWindow أي يحسب المرسل نافذة فعالة تحد من مقدار البيانات الممكن إرسالها: EffectiveWindow = AdvertisedWindow - (LastByteSent - LastByteAcked) يجب أن يكون EffectiveWindow أكبر من 0 قبل أن يتمكن المصدر من إرسال المزيد من البيانات. لذلك من الممكن أن يصل جزءٌ ما للإقرار بـ x بايت، مما يسمح للمرسل بزيادة LastByteAcked بمقدار x، ولكن نظرًا لأن عملية الاستلام لم تكن تقرأ أي بيانات، فإن النافذة المُعلن عنها أصبحت الآن أصغر بمقدار x بايت من الوقت السابق، وبالتالي سيتمكّن المرسل من تحرير مساحة المخزن المؤقت، مع عدم إرسال أي بيانات أخرى. يجب أن يتأكد جانب الإرسال أيضًا طوال هذا الوقت من أن عملية التطبيق المحلي لا تتجاوز مخزن الإرسال المؤقت، أي: LastByteWritten - LastByteAcked <= MaxSendBuffer إذا حاولت عملية الإرسال كتابة y بايت إلى TCP، ولكن: (LastByteWritten - LastByteAcked) + y> MaxSendBuffer عندها سيوقف بروتوكول TCP عملية الإرسال ولا يسمح لها بتوليد المزيد من البيانات. أصبح من الممكن الآن فهم كيف تؤدي عملية الاستلام البطيئة إلى إيقاف عملية الإرسال السريعة في النهاية. أولًا، يمتلئ مخزن الاستقبال المؤقت، مما يعني تقلص النافذة المعلن عنها إلى 0. وتعني النافذة المعلن عنها بالقيمة 0 أن جانب الإرسال لا يمكنه نقل أي بيانات، بالرغم من الإقرار بالبيانات التي أرسلها سابقًا بنجاح. أخيرًا، يدل عدم القدرة على نقل أي بيانات على إمتلاء مخزن الإرسال المؤقت، مما يؤدي في النهاية إلى إيقاف بروتوكول TCP لعملية الإرسال. يكون بروتوكول TCP قادرًا على فتح نافذته احتياطيًا بمجرد أن تبدأ عملية الاستلام في قراءة البيانات مرةً أخرى، مما يسمح لبروتوكول TCP في طرف الإرسال بنقل البيانات من المخزن المؤقت الخاص به. ، تُزاد قيمة LastByteAcked عندما يجري الإقرار بهذه البيانات في النهاية، وتصبح مساحة المخزن المؤقت التي تحتوي على هذه البيانات المعترف بها حرة، ويُلغى إيقاف عملية الإرسال ويُسمَح لها بالمتابعة. هناك تفصيلٌ متبقٍ يجب حله، وهو كيف يعرف الجانب المرسل أن النافذة المُعلن عنها لم تعد 0؟ يرسل بروتوكول TCP دائمًا جزء segment استجابةً لجزء البيانات المستلمة، وتحتوي هذه الاستجابة على أحدث القيم للحقلين Acknowledge و AdvertisedWindow، حتى في حال عدم تغيُّر هذه القيم منذ آخر مرة أُرسلت فيها، وهذه هي المشكلة. لا يُسمَح للمرسل بإرسال أي بيانات أخرى بمجرد أن يعلن جانب الاستلام عن نافذة بحجم 0، مما يعني أنه ليس لديه طريقة لاكتشاف أن النافذة المُعلن عنها لم تعد 0 في وقت ما في المستقبل. لا يرسل بروتوكول TCP على جانب الاستقبال أجزاءًا بلا بيانات nondata تلقائيًا، إنما يرسلها فقط استجابةً لجزء بياناتٍ واصلة. يتعامل بروتوكول TCP مع هذا الوضع على النحو التالي: يستمر جانب الإرسال في إرسال جزء بحجم بايتٍ واحد من البيانات بين الحين والآخر عندما يعلن الجانب الآخر عن نافذة بحجم 0. يعلم جانب الإرسال أن هذه البيانات لن تُقبل على الأرجح، لكنه يحاول على أية حال، نظرًا لتنبيه trigger كل جزء من هذه الأجزاء المكونة من 1 بايت استجابةً تحتوي على النافذة المعلن عنها حاليًا. في النهاية، ينبّه triggers أحد هذه المسابر المكونة من 1 بايت استجابةً تبلّغ عن نافذةٍ غير صفرية معلن عنها. لاحظ أن هذه الرسائل ذات 1 بايت تسمى مسابر النافذة الصفرية Zero Window Probes وعمليًا يجري إرسالها كل 5 إلى 60 ثانية. أما بالنسبة للبايت المفرد من البيانات الذي سيُرسل في المسبار: فهو البايت التالي للبيانات الفعلية الموجودة خارج النافذة مباشرةً (يجب أن تكون بيانات حقيقية إذا قبِلها المستقبل). لاحظ أن السبب الذي يجعل الجانب المرسل يرسل جزء المسبار هذا دوريًا هو أن بروتوكول TCP مصمم لجعل جانب الاستقبال بسيطًا قدر الإمكان، فهو ببساطة يستجيب لأجزاء من المرسل، ولا يبدأ أي إجراءٍ بمفرده. هذا مثال على قاعدة تصميم بروتوكولٍ معترف بها (على الرغم من عدم تطبيقها عالميًا)، والتي نطلق عليها قاعدة المرسل الذكي / المستقبل الغبي smart sender/ dumb receiver بسبب عدم وجود اسمٍ أفضل. الحماية من الالتفاف Protecting Against Wraparound يأخذ هذا القسم والقسم التالي في الاعتبار حجم حقلي SequenceNum وAdvertisedWindow وتأثيرات أحجامهما على أداء وصحة بروتوكول TCP. يبلغ طول الحقل SequenceNum الخاص ببروتوكول TCP 32 بتًا، بينما يبلغ طول الحقل AdvertisedWindow 16 بتًا. مما يعني أن بروتوكول TCP قد استوفى بسهولة متطلبات خوارزمية النافذة المنزلقة بحيث تكون مساحة الرقم التسلسلي ضعف حجم النافذة: 232 >> 2 × 216، ومع ذلك فإن هذا المطلب ليس بالشيء المهم لهذين الحقلين، وبإمكانك افتراض كل حقلٍ على حدة. تكمن أهمية مساحة الرقم التسلسلي 32 بت في أن الرقم التسلسلي المُستخدَم في اتصالٍ معين قد يلتف، أي يمكن إرسال بايتٍ برقمٍ تسلسلي S في وقت واحد، ثم قد يرسَل بايتٌ ثانٍ بنفس الرقم التسلسلي S في وقت لاحق. نفترض أن الرزم لا يمكنها البقاء على الإنترنت لفترة أطول من MSL الموصى بها، وبالتالي نحتاج حاليًا إلى التأكد من أن الرقم التسلسلي لا يلتف خلال فترة زمنية مدتها 120 ثانية. يعتمد حدوث ذلك أم لا على مدى سرعة نقل البيانات عبر الإنترنت، أي مدى السرعة التي يمكن بها استهلاك مساحة الرقم التسلسلي 32 بت. (تفترض هذه المناقشة أننا نحاول استهلاك مساحة الرقم التسلسلي بأسرع ما يمكن، ولكن بالطبع سنكون كذلك إذا كنا نقوم بعملنا المتمثل في الحفاظ على الأنبوب ممتلئًا) يوضح الجدول التالي المدة التي يستغرقها الرقم التسلسلي للالتفاف حول الشبكات ذات أحياز النطاق التراسلي bandwidths المختلفة. table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } حيز النطاق التراسلي Bandwidth الوقت اللازم للالتفاف Time until Wraparound T1 مقداره 1.5 ميجابت في الثانية 6.4 ساعات T3 مقداره 45 ميجابت في الثانية 13 دقيقة Fast Ethernet مقداره 100 ميجابت في الثانية 6 دقائق OC-3 مقداره 155 ميجابت في الثانية 4 دقائق OC-48 مقداره 2.5 جيجابت في الثانية 14 ثانية OC-192 مقداره 10 جيجابت في الثانية 3 ثوان 10GigE مقداره 10 جيجابت في الثانية 3 ثوان كما ترى مساحة الرقم التسلسلي 32 بت كافيةٌ عند حيز نطاقٍ تراسلي متواضع، ولكن بالنظر إلى أن روابط OC-192 أصبحت شائعة الآن في الشبكات الرئيسية backbone للإنترنت، وأن معظم الخوادم تأتي الآن بواجهات 10Gig Ethernet (أو 10 جيجابت في الثانية)، فنكون قد تجاوزنا الآن هذه النقطة وسيكون 32 بتًا صغيرًا جدًا. عملت مؤسسة IETF على توسيع بروتوكول TCP الذي يوسّع بفعاليةٍ مساحة الرقم التسلسلي للحماية من التفاف الرقم التسلسلي. الحفاظ على الأنبوب ممتلئا Keeping the Pipe Full تكمن أهمية حقل AdvertisedWindow ذي 16 بتًا في أنه يجب أن يكون كبيرًا بما يكفي للسماح للمرسل بالحفاظ على الأنبوب ممتلئًا. من الواضح أن جهاز الاستقبال حر في عدم فتح النافذة بالحجم الذي يسمح به حقل AdvertisedWindow، فنحن مهتمون بالحالة التي يكون فيها لدى المستقبل مساحة تخزين كافية للتعامل مع أكبر قدرٍ ممكن من البيانات المسموح بها AdvertisedWindow. لا يُحدَّد حجم حقل AdvertisedWindow في هذه الحالة بحيز النطاق الشبكة التراسلي فقط ولكن بجداء التأخير × حيز النطاق التراسلي بدلًا من ذلك، والذي يجب فتحه بصورةٍ كافية للسماح بإرسال الجداء الكامل للتأخير × حيز النطاق التراسلي للبيانات المُراد إرسالها. بافتراض أن RTT تبلغ 100 ميلي ثانية (رقم نموذجي للاتصال عبر البلاد في الولايات المتحدة)، يعطي الجدول التالي جداء التأخير × حيز النطاق التراسلي للعديد من تقنيات الشبكة: حيز النطاق التراسلي Bandwidth جداء التأخير × حيز النطاق التراسلي Delay × Bandwidth Product T1 مقداره 1.5 ميجابت في الثانية 18 كيلوبايت T3 مقداره 45 ميجابت في الثانية 549 كيلوبايت Fast Ethernet مقداره 100 ميجابت في الثانية 1.2 ميجا بايت OC-3 مقداره 155 ميجابت في الثانية 1.8 ميجا بايت OC-48 مقداره 2.5 جيجابت في الثانية 29.6 ميجا بايت OC-192 مقداره 10 جيجابت في الثانية 118.4 ميجا بايت 10GigE مقداره 10 جيجابت في الثانية 118.4 ميجا بايت كما ترى فإن حقل AdvertisedWindow الخاص ببروتوكول TCP في حالةٍ أسوأ من حقل SequenceNum الخاص به، فهو ليس كبيرًا بما يكفي للتعامل حتى مع اتصال T3 عبر الولايات المتحدة القارية، نظرًا لأن الحقل 16 بت يسمح لنا بالإعلان عن نافذة حجمها 64 كيلو بايت فقط. يوفر توسيع TCP نفسه المذكور أعلاه آليةً لزيادة حجم النافذة المعلن عنها بفعالية. بهذا نكون قد تعرفنا على برتوكولات تدفق البايتات الموثوقة في الشبكات الحاسوبية آخذين بروتوكول TCP مثالًا في ذلك، وسنتابع بالمقال الموالي شرحها بالتخصيص في آليات الإرسال والبدائل. ترجمة -وبتصرّف- للقسم Reliable Byte Stream من فصل ProtocolsEnd-to-End من كتاب Computer Networks: A Systems Approach. اقرأ أيضًا بروتوكولات طرف إلى طرف End-to-End Protocols في الشبكات الحاسوبيةالمقال السابق: تأسيس الشبكات الحاسوبية والتعرف على تطبيقاتها البرمجيات المستخدمة في بناء الشبكات الحاسوبية التوجيه Routing بين الأجهزة المتنقلة في الشبكات الحاسوبية دليل بصري لكيفية استخدام أنفاق SSH
  3. يدعم TCP تجريد تدفق البايت، أي أن برامج التطبيقات تكتب البايت في التدفق، والأمر متروك لبروتوكول TCP لاتخاذ القرار بأن لديه بايتات كافية لإرسال جزء. لكن ما هي العوامل التي تحكم هذا القرار؟ سنتابع في هذا المقال ما تحدثنا عنه في المقال السابق حول بروتوكولات تدفق البايتات الموثوقة بالاعتماد على مثال TCP، وسنكمل الحديث هنا عن هذه البروتوكولات آخذين جانب آلية الإرسال والبدائل. إذا تجاهلنا إمكانية التحكم في التدفق، أي بفرض أن النافذة مفتوحة على مصراعيها كما هو الحال عند بدء الاتصال لأول مرة، عندئذ يكون لدى بروتوكول TCP ثلاث آليات لبدء إرسال جزء. أولًا، يحتفظ بروتوكول TCP بمتغير، يسمى عادةً حجم الجزء الأقصى maximum segment size أو اختصارًا MSS، ويرسل جزءًا بمجرد أن يجمع بايتاتٍ بحجم MSS من عملية الإرسال. يُضبَط MSS على حجم أكبر جزء يمكن أن يرسله TCP دون التسبب في تجزئة عنوان IP المحلي. أي ضُبِط MSS على أقصى وحدة إرسال maximum transmission unit أو اختصارًا MTU للشبكة المتصلة مباشرة، مطروحًا منها حجم ترويستي TCP و IP. الشيء الثاني الذي ينبّه بروتوكول TCP لإرسال جزء هو أن عملية الإرسال طلبت منه صراحةً القيام بذلك. يدعم بروتوكول TCP العملية push، وتستدعي عملية الإرسال هذه العملية لتفريغ flush المخزن المؤقت للبايتات غير المرسلة بفعالية. المنبّه trigger الأخير لإرسال جزء هو إنطلاق المؤقت timer، حيث يحتوي الجزء الناتج على العديد من البايتات المخزَّنة حاليًا للإرسال. وعلى أية حال لن يكون هذا "المؤقت" كما تتوقعه بالضبط كما سنرى قريبًا. متلازمة النوافذ الساذجة Silly Window Syndrome لا يمكننا بالتأكيد تجاهل التحكم في التدفق فقط، والذي يلعب دورًا واضحًا في تقييد المرسل. فإذا كان لدى المرسل بايتات بحجم MSS من البيانات لإرسالها وكانت النافذة مفتوحة على الأقل بهذا القدر، فإن المرسل يرسل جزءًا كاملًا. افترض أن المرسل يجمّع البايتات لإرسالها، ولكن النافذة مغلقة حاليًا. لنفترض الآن وصول ACK يفتح النافذة بفعالية بما يكفي لإرسال المرسل، بحجم MSS / 2 بايت مثلًا. هل يجب على المرسل إرسال جزء نصف ممتلئ أم الانتظار حتى تفتح النافذة بحجم MSS كامل؟ لم تذكر المواصفات الأصلية شيئًا بشأن هذه النقطة، وقررت عمليات التنفيذ الأولي لبروتوكول TCP المضي قدمًا وإرسال جزء نصف كامل، ولكن ليس هناك ما يخبرنا إلى متى سيبقى بهذه الحالة قبل أن تفتح النافذة أكثر. اتضح أن استراتيجية الاستفادة الكبيرة من أية نافذةٍ متاحة تؤدي إلى حالة تُعرَف الآن باسم متلازمة النوافذ الساذجة، حيث يساعد الشكل التالي على تصور ما يحدث: إذا فكّرت في تدفق TCP على أنه حزامٌ ناقل به حاويات "ممتلئة" أو أجزاء بيانات data segments تسير في اتجاهٍ واحد وحاويات فارغة هي الإشعارات ACKs تسير في الاتجاه العكسي. فإن الأجزاء ذات الحجم MSS تتوافق مع الحاويات الكبيرة والأجزاء ذات الحجم 1 بايت تتوافق مع حاويات صغيرة جدًا. طالما أن المرسل يرسل أجزاءًا بحجم MSS ويرسل المستقبل إشعارات ACKs على الأقل بحجم MSS من البيانات في كل مرة، وبالتالي سيعمل كل شيء جيدًا (كما في القسم أ من الشكل الآتي). لكن ماذا لو اضطر المستقبل إلى تقليل النافذة بحيث لا يتمكن المرسل في وقتٍ ما من إرسال MSS كامل من البيانات؟ إذا ملأ المرسل حاويةً فارغة حجمها أصغر من MSS بمجرد وصولها، فسيرسل المستقبل إشعارًا بهذا العدد الأصغر من البايتات، وبالتالي تظل الحاوية الصغيرة المدخلة في النظام إلى أجلٍ غير مسمى فيه، أي أنها تُملَأ وتُفرَغ على الفور عند كل طرف ولا تُدمَج مطلقًا مع الحاويات المجاورة لإنشاء حاويات أكبر، كما في القسم (ب) من الشكل التالي. اُكتشِف هذا السيناريو عندما وجدت التطبيقات الأولى لبروتوكول TCP نفسها تملأ الشبكة بأجزاء صغيرة. تُعتبر متلازمة النوافذ الساذجة مشكلةً فقط عندما يرسل المرسل جزءًا صغيرًا أو يفتح جهاز الاستقبال النافذة بمقدار صغير. إذا لم يحدث أي من هذين الأمرين، فلن تُدخَل الحاوية الصغيرة مطلقًا في التدفق. من غير الممكن تحريم إرسال مقاطع صغيرة، فقد يقوم التطبيق بعملية push بعد إرسال بايت واحد على سبيل المثال، ولكن من الممكن منع جهاز الاستقبال من إدخال حاوية صغيرة (أي نافذة صغيرة مفتوحة)، حيث يجب أن ينتظر جهاز الاستقبال حيزًا يساوي حجم MSS قبل أن يعلن عن نافذة مفتوحة، وذلك بعد الإعلان عن نافذةٍ صفرية. نظرًا لإمكانية استبعاد إمكانية إدخال حاويةٍ صغيرة في التدفق، إلا أننا نحتاج أيضًا إلى آلياتٍ لدمجها. يمكن للمستقبل القيام بذلك عن طريق تأخير ACK، أي إرسال ACK واحد مدمج بدلًا من إرسال عدة رسائلٍ أصغر، ولكن هذا حل جزئي فقط لأن جهاز الاستقبال ليس لديه طريقة لمعرفة المدة التي يمكن فيها تأخير انتظار وصول جزء آخر أو انتظار التطبيق لقراءة المزيد من البيانات (وبالتالي فتح النافذة). يقع الحل النهائي على عاتق المرسل، وهو ما يعيدنا إلى مشكلتنا الأصلية: متى يقرر مرسل TCP إرسال جزءsegment؟ خوارزمية Nagle بالعودة إلى مرسل TCP، إذا كانت هناك بياناتٌ لإرسالها ولكن النافذة مفتوحة بحجمٍ أقل من حجم MSS، فقد نرغب في الانتظار بعض الوقت قبل إرسال البيانات المتاحة، ولكن السؤال هو كم طول هذه المدة؟ إذا انتظرنا وقتًا طويلًا، فإننا نُضِر بذلك التطبيقات التفاعلية مثل تطبيق Telnet. وإذا لم ننتظر طويلًا بما فيه الكفاية، فإننا نجازف بإرسال مجموعةٍ من الرزم الصغيرة والوقوع في متلازمة النوافذ الساذجة. الجواب هو إدخال مؤقت timer والإرسال عند انتهاء مدته. يمكننا استخدام مؤقت قائم على النبضات، مثل مؤقت ينطلق كل 100 ميلي ثانية. قدّم Nagle حلًا رائعًا للتوقيت الذاتي. الفكرة هي أنه طالما لدى بروتوكول TCP أي بيانات قيد الإرسال in flight، فإن المرسل سيتلقى في النهاية ACK. يمكن التعامل مع ACK كإطلاق مؤقت، ينبِّه بإرسال المزيد من البيانات. توفر خوارزمية Nagle قاعدة بسيطة وموحدة لاتخاذ قرار بشأن توقيت بدء الإرسال. When the application produces data to send if both the available data and the window >= MSS send a full segment else if there is unACKed data in flight buffer the new data until an ACK arrives else send all the new data now من المقبول دائمًا إرسال جزء كامل إذا سمحت النافذة بذلك، كما يحق لك إرسال كمية صغيرة من البيانات على الفور إذا لم يكن هناك أي أجزاء قيد النقل. ولكن إذا كان هناك أي شيء قيد الإرسال، فيجب على المرسل انتظار ACK قبل إرسال الجزء التالي. وبالتالي سيرسل تطبيقٌ تفاعلي مثل Telnet الذي يكتب باستمرار بايتًا واحدًا في المرة الواحدة البياناتِ بمعدل جزء واحد لكل RTT. ستحتوي بعض الأجزاء على بايت واحد، بينما يحتوي البعض الآخر على عدد بايتات بقدر ما كان المستخدم قادرًا على الكتابة في وقت واحد ذهابًا وإيابًا. تسمح واجهة المقبس للتطبيق بإيقاف تشغيل خوارزمية Nagel عن طريق تعيين خيار TCP_NODELAY نظرًا لأن بعض التطبيقات لا يمكنها منح مثل هذا التأخير لكل عملية كتابة تقوم بها لاتصالٍ من نوع TCP، حيث يعني ضبط هذا الخيار إرسال البيانات في أسرع وقتٍ ممكن. إعادة الإرسال التكيفي Adaptive Retransmission بما أن بروتوكول TCP يضمن التسليم الموثوق للبيانات، فإنه يعيد إرسال كل جزء إذا لم يُستلَم إشعار ACK خلال فترةٍ زمنية معينة. يضبط بروتوكول TCP هذه المهلة الزمنية كدالة لوقت RTT الذي يتوقعه بين طرفي الاتصال. إن اختيار قيمة المهلة الزمنية المناسبة ليس بهذه السهولة لسوء الحظ، نظرًا لمجال قيم RTT الممكنة بين أي زوجٍ من المضيفين في الإنترنت، بالإضافة إلى الاختلاف في RTT بين نفس المضيفين بمرور الوقت. لمعالجة هذه المشكلة، يستخدم بروتوكول TCP آلية إعادة الإرسال التكيفية. سنقوم بوصف هذه الآلية الآن وتطورها مع مرور الوقت حيث اكتسب مجتمع الإنترنت المزيد من الخبرة باستخدام TCP. الخوارزمية الأصلية نبدأ بخوارزمية بسيطة لحساب قيمة المهلة الزمنية timeout بين زوجٍ من المضيفين. هذه هي الخوارزمية التي وُصفت في الأصل في مواصفات بروتوكول TCP، ولكن يمكن استخدامها بواسطة أي بروتوكول طرفٍ إلى طرف end-to-end protocol. تكمن الفكرة في الاحتفاظ بمتوسط تشغيل RTT ثم حساب المهلة الزمنية كدالة لوقت RTT. يسجل TCP الوقت في كل مرة يرسل فيها جزء بيانات، ويقرأ بروتوكول TCP الوقت مرةً أخرى عند وصول ACK لهذا الجزء، ثم يأخذ الفرق بين هاتين المرتين على أنه SampleRTT. ثم يحسب TCP قيمة EstimatedRTT كمتوسط مرجح بين التقدير estimate السابق وهذه العينة sampleالجديدة، حيث: EstimatedRTT = alpha x EstimatedRTT + (1 - alpha) x SampleRTT يُحدَّد المعامل alpha لتنعيم EstimatedRTT. تتتبّع قيمةُ alpha الصغيرة التغييراتِ في RTT ولكن ربما تتأثر بشدة بالتقلبات المؤقتة. ومن ناحيةٍ أخرى، تكون قيمة alpha الكبيرة أكثر استقرارًا ولكن ربما لا تكون سريعة بما يكفي للتكيف مع التغييرات الحقيقية. أوصت مواصفات TCP الأصلية بإعداد قيمة alpha بين 0.8 و 0.9. يستخدم بروتوكول TCP المعامل EstimatedRTT لحساب المهلة الزمنية timeout بطريقة متحفظة إلى حدٍ ما: TimeOut = 2 x EstimatedRTT خوارزمية كارن / بارتريدج Karn/Partridge اُكتشِف عيبٌ واضح في الخوارزمية السابقة البسيطة بعد عدة سنوات من الاستخدام على الإنترنت. كانت المشكلة أن الإشعار ACK لا يُقِر حقًا بالإرسال، بل باستلام البيانات في الحقيقة، أي عندما يُعاد إرسال جزء ثم وصول ACK إلى المرسل، فمن المستحيل تحديد ما إذا كان ينبغي إرفاق هذا الإشعار بالإرسال الأول أو الثاني للجزء بغرض قياس sample RTT. من الضروري معرفة الإرسال الذي سيُربَط به لحساب العينة SampleRTT بدقة. إذا افترضت أن الإشعار ACK للإرسال الأصلي ولكنه كان في الحقيقة للثاني كما هو موضح في الشكل التالي، فإن SampleRTT كبيرةٌ جدًا كما في القسم (أ) من الشكل التالي، وإذا افترضت أن الإشعار ACK للإرسال الثاني ولكنه كان في الواقع للإرسال الأول، فإن عينة SampleRTT صغيرةٌ جدًا كما في القسم (ب) من الشكل التالي: الحل الذي جرى اقتراحه في عام 1987 بسيط جدًا: يتوقف بروتوكول TCP عن أخذ عيناتٍ من RTT عندما يعيد إرسال جزء ما، فهو يقيس SampleRTT فقط للأجزاء التي أُرسلت مرةً واحدة فقط. يُعرف هذا الحل باسم خوارزمية كارن / بارتريدج تيّمنًا بمخترعَيها. يتضمن الإصلاح المقترح أيضًا تغييرًا صغيرًا ثانيًا في آلية مهلة TCP الزمنية. يضبط بروتوكول TCP المهلة التالية لتكون ضعف آخر مهلة في كل مرة يعيد فيها الإرسال، بدلًا من إسنادها إلى آخرEstimatedRTT. اقترح كارن و بارتريدج أن يستخدم بروتوكولُ TCP تراجعًا أسيًا exponential backoff، على غرار ما يفعله إيثرنت. الدافع لاستخدام التراجع الأسي بسيط: الازدحام هو السبب الأكثر احتمالًا لفقدان الأجزاء، مما يعني أن مصدر TCP يجب ألا يتفاعل بقوة مع انقضاء المهلة الزمنية، فكلما انقضت مهلة الاتصال الزمنية، يجب أن يصبح المصدر أكثر حذرًا. سنرى هذه الفكرة مرة أخرى، مجسَّدةً في آلية أعقد لاحقًا. خوارزمية جاكوبسون / كارلس Jacobson/Karels قُدّمت خوارزمية كارن / بارتريدج في وقت كان فيه الإنترنت يعاني من مستويات عالية من ازدحام الشبكة، فصُمِّم نهج هذه الخوارزمية لإصلاح بعض أسباب هذا الازدحام، ولكن على الرغم من تقديمها تحسينًا، إلا أنه لم يتم القضاء على الازدحام. اقترح باحثان آخران (جاكوبسون وكارلس) تغييرًا جذريًا في بروتوكول TCP لمحاربة الازدحام في العام التالي (1988). سنركز على هذا الاقتراح المتعلق بتحديد انتهاء المهلة وإعادة إرسال جزء ما. يجب أن يكون ارتباط آلية المهلة الزمنية بالازدحام واضحًا، فإذا انقضت المهلة في وقت مبكرٍ جدًا، فقد تعيد إرسال جزء بدون داعٍ، والذي يضيف فقط إلى الحِمل على الشبكة. السبب الآخر للحاجة إلى قيمة مهلة دقيقة هو أن المهلة تُؤخَذ للإشارة إلى الازدحام، مما يؤدي إلى تشغيل آلية للتحكم في الازدحام. أخيرًا، لاحظ أنه لا يوجد شيء يتعلق بحساب مهلة جاكوبسون وكارلس الخاص ببروتوكول TCP. يمكن استخدامه من قبل أي بروتوكول من طرفٍ إلى طرف. تكمن المشكلة الرئيسية في حساب الخوارزمية الأصلية في أنها لا تأخذ في الاعتبار تباين عينات RTT. إذا كان الاختلاف بين العينات صغيرًا، فيمكن الوثوق في EstimatedRTT بصورةٍ أفضل ولا يوجد سبب لضرب هذا التقدير في 2 لحساب المهلة الزمنية. يشير التباين الكبير في العينات من ناحية أخرى إلى أن قيمة المهلة لا ينبغي أن تكون مرتبطة بإحكام شديد بقيمة EstimatedRTT. بينما في النهج الجديد، يقيس المرسل SampleRTT جديدًا كما كان يتم سابقًا، ثم تُطوى هذه العينة الجديدة في حساب المهلة على النحو التالي: Difference = SampleRTT - EstimatedRTT EstimatedRTT = EstimatedRTT + ( delta x Difference) Deviation = Deviation + delta (|Difference| - Deviation) حيث تقع قيمة delta بين 0 و1. أي أننا نحسب كلًا من متوسط وقت RTT والتغير في هذا المتوسط، ثم يحسب TCP قيمة المهلة كدالة لكل من EstimatedRTT وDeviation على النحو التالي: TimeOut = mu x EstimatedRTT + phi x Deviation يُعيَّن mu على القيمة 1 وphi على القيمة 4 بناءً على الخبرة. وهكذا تكون TimeOut قريبةً من EstimatedRTT عندما يكون التباين صغيرًا، حيث يؤدي التباين الكبير إلى سيطرة مصطلح الانحراف Deviation على الحساب. التطبيق Implementation هناك نقطتان جديرتان بالملاحظة فيما يتعلق بتطبيق المهلات الزمنية في بروتوكول TCP. الأولى هي إمكانية تطبيق حساب EstimatedRTT وDeviation دون استخدام حساب الأعداد العشرية. فبدلًا من ذلك، تُوسَّع العملية الحسابية بالكامل بمقدار 2n، مع اختيار delta لتكون 1/2n. يتيح لنا ذلك إجراء العمليات الحسابية الصحيحة، وتطبيق الضرب والقسمة باستخدام الإزاحات shifts، وبالتالي تحقيق أداءٍ أعلى. يُعطى الحساب الناتج عن طريق جزء الشيفرة التالية، حيث n = 3 (أي delta = 1/8). لاحظ تخزين EstimatedRTT وDeviation في نماذجهما الموسّعَين، بينما قيمة SampleRTT في بداية الشيفرة و TimeOut في النهاية هي قيمٌ حقيقية غير مُوسَّعة. إذا وجدت صعوبة في تتبع الشيفرة، فقد ترغب في محاولة إدخال بعض الأرقام الحقيقية فيها والتحقق من أنها تعطي نفس النتائج مثل المعادلات أعلاه: { SampleRTT -= (EstimatedRTT >> 3); EstimatedRTT += SampleRTT; if (SampleRTT < 0) SampleRTT = -SampleRTT; SampleRTT -= (Deviation >> 3); Deviation += SampleRTT; TimeOut = (EstimatedRTT >> 3) + (Deviation >> 1); } النقطة الثانية هي أن خوارزمية جاكوبسون / كارلس جيدة فقط مثل الساعة المستخدمة لقراءة الوقت الحالي. كانت دقة الساعة كبيرة في تطبيقات يونيكس النموذجية في ذلك الوقت حيث وصلت إلى 500 ميلي ثانية، وذلك أكبر بكثير من متوسط RTT عبر دولة في مكانٍ ما والذي يكون بين 100 و200 ميلي ثانية. لجعل الأمور أسوأ، فحص تطبيق يونيكس لبروتوكول TCP فيما إذا كان يجب أن يحدث انتهاء للمهلة في كل مرة تُحدَّد فيها الساعة 500 ميلي ثانية ولن يأخذ سوى عينةٍ من وقت الذهاب والإياب مرة واحدة لكل RTT. و قد يعني الجمع بين هذين العاملين أن المهلة ستحدث بعد ثانية واحدة من إرسال الجزء. تشتمل توسّعات TCP على آلية تجعل حساب RTT هذا أدق قليلًا. تستند جميع خوارزميات إعادة الإرسال التي ناقشناها إلى مهلات الإشعارات، والتي تشير إلى احتمال فقد جزءٍ ما. لاحظ أن المهلة لا تخبر المرسل فيما إذا اُستلِمت بنجاحٍ أي أجزاءٍ أرسلها بعد فقدان جزء، وذلك لأن إشعارات TCP تراكمية cumulative، أي أنها تحدد فقط الجزء الأخير المُستلم دون أي فجوات سابقة. ينمو استقبال الأجزاء التي تظهر بعد زيادة الفجوة بصورةٍ متكررة كما تؤدي الشبكات الأسرع إلى نوافذ أكبر. إذا أخبرت ACK المرسلَ أيضًا عن الأجزاء اللاحقة، إن وجدت، فقد يكون المرسل أذكى بشأن الأجزاء التي يعيد إرسالها، إضافةً لاستخلاص استنتاجات أفضل حول حالة الازدحام، وإجراء تقديرات RTT أفضل. حدود السجلات Record Boundaries ليس بالضرورة أن يكون عدد البايتات التي يكتبها المرسل نفس عدد البايتات التي قرأها المستقبل نظرًا لأن بروتوكول TCP هو بروتوكول تدفق بايتات، فقد يكتب التطبيق 8 بايتات، ثم 2 بايت، ثم 20 بايت إلى اتصال TCP، بينما يقرأ التطبيق على جانب الاستقبال 5 بايتات في المرة الواحدة داخل حلقة تتكرر 6 مرات. لا يقحم بروتوكول TCP حدود السجلات بين البايتين الثامن والتاسع، ولا بين البايتين العاشر والحادي عشر. هذا على عكس بروتوكول الرسائل الموّجهة، مثل بروتوكول UDP، حيث تكون الرسالة المرسَلة بنفس طول الرسالة المُستلَمة. يمتلك بروتوكول TCP، على الرغم من كونه بروتوكول تدفق بايتات، ميزتين مختلفتين يمكن للمرسل استخدامهما لإدراج حدود السجلات في تدفق البايتات هذا، وبالتالي إعلام المستقبِل بكيفية تقسيم تدفق البايتات إلى سجلات. (تُعَد القدرة على تحديد حدود السجلات أمرًا مفيدًا في العديد من تطبيقات قواعد البيانات على سبيل المثال) وضُمِّنت هاتان الميزتان في الأصل في بروتوكول TCP لأسباب مختلفة تمامًا، ثم جرى استخدامهما لهذا الغرض بمرور الوقت، وهما: الآلية الأولى هي ميزة البيانات العاجلة urgent data feature، التي طُبِّقت بواسطة الراية URG وحقل UrgPtr في ترويسة TCP. صُمِّمت آلية البيانات العاجلة في الأصل للسماح للتطبيق المرسِل بإرسال بيانات خارج النطاق إلى نظيره. نعني بعبارة خارج النطاق out-of-band البياناتِ المنفصلة عن تدفق البيانات الطبيعي (أمر مقاطعة عملية جارية بالفعل على سبيل المثال). حُدِّدت هذه البيانات خارج النطاق في الجزء segment باستخدام حقل UrgPtr وكان من المقرر تسليمها إلى عملية الاستلام بمجرد وصولها، حتى لو عنى ذلك تسليمها قبل بيانات ذات رقمٍ تسلسلي سابق. لكن، لم تُستخدَم هذه الميزة بمرور الوقت، لذلك بدلًا من الإشارة إليها ببيانات "عاجلة" ، فقد اُستخدمت للدلالة على بيانات "خاصة"، مثل علامة السجل record marker. طُوِّر هذا الاستخدام لأنه، كما هو الحال مع عملية push، يجب على TCP على الجانب المستلم إبلاغ التطبيق بوصول بيانات عاجلة، أي أن البيانات العاجلة في حد ذاتها ليست مهمة. إنها تشير إلى حقيقة أن عملية الإرسال يمكنها إرسال إشارة فعالة إلى جهاز الاستقبال بأن هناك أمرٌ مهم. الآلية الثانية لإدخال علامات نهاية السجلات في بايت هي العملية push. صُمِّمت هذه الآلية في الأصل للسماح لعملية الإرسال بإعلام بروتوكول TCP بأنه يجب عليه إرسال أو تفريغ flush أي بايتات جمَّعها لنظيره. يمكن استخدام العملية push لتطبيق حدود السجلات لأن المواصفات تنص على أن بروتوكول TCP يجب أن يرسل أي بيانات مخزَّنة مؤقتًا عند المصدر عندما يقول التطبيق push، ويُعلِم بروتوكول TCP، اختياريًا، التطبيق في الوجهة عند وجود جزء وارد يحتوي على مجموعة الراية PUSH. إذا كان جانب الاستقبال يدعم هذا الخيار (لا تدعم واجهة المقبس هذا الخيار)، فيمكن عندئذٍ استخدام العملية push لتقسيم تدفق TCP إلى سجلات. برنامج التطبيق حرٌ دائمًا بحشر حدود السجلات دون أي مساعدة من بروتوكول TCP، حيث يمكنه إرسال حقل يشير إلى طول السجل الذي يجب أن يتبعه، أو يمكنه حشر علامات حدود السجل الخاصة به في تدفق البيانات على سبيل المثال. إضافات بروتوكول TCP لقد ذكرنا أن هناك إضافات Extensions لبروتوكول TCP تساعد في التخفيف من بعض المشاكل التي واجهها حيث أصبحت الشبكة الأساسية أسرع. صُمِّمت هذه الإضافات ليكون لها تأثيرٌ ضئيل على بروتوكول TCP قدر الإمكان، حيث تُدرَك كخياراتٍ يمكن إضافتها إلى ترويسة TCP. (سبب احتواء ترويسة TCP على حقل HdrLen هو أن الترويسة يمكن أن تكون بطول متغير، يحتوي الجزء المتغير من ترويسة TCP على الخيارات المُضافة). أهمية إضافة هذه الإضافات كخيارات بدلًا من تغيير جوهر ترويسة TCP هو أن المضيفين لا يزالون قادرين على التواصل باستخدام TCP حتى لو لم يقوموا بتطبيق الخيارات، ولكن يمكن للمضيفين الذين يقومون بتطبيق الإضافات الاختيارية الاستفادة منها. يتفق الجانبان على استخدام الخيارات أثناء مرحلة إنشاء اتصال TCP. تساعد الإضافة الأولى على تحسين آلية ضبط مهلة TCP الزمنية. يمكن لبروتوكول TCP قراءة ساعة النظام الفعلية عندما يكون على وشك إرسال جزء بدلًا من قياس RTT باستخدام حدث قاسٍ coarse-grained، ووضع هذا الوقت (فكر به على أنه علامة زمنية timestamp مؤلفة من 32 بتًا) في ترويسة الجزء، ثم يُرجِع echoes المستقبل هذه العلامة الزمنية مرةً أخرى إلى المرسل في إشعاره، ويطرح المرسل هذه العلامة الزمنية من الوقت الحالي لقياس وقت RTT. يوفر خيار العلامة الزمنية مكانًا مناسبًا لبروتوكول TCP لتخزين سجل وقت إرسال جزء، أي يخزن الوقت في الجزء نفسه. لاحظ أن نقاط النهاية في الاتصال لا تحتاج إلى ساعات متزامنة، حيث تُكتَب العلامة الزمنية وتُقرَأ في نهاية الاتصال نفسها. تعالج الإضافة الثانية مشكلة التفاف wrapping around حقل SequenceNum المكون من 32 بت لبروتوكول TCP في وقت قريب جدًا على شبكة عالية السرعة. يستخدم بروتوكول TCP العلامة الزمنية المؤلفة من 32 بتًا الموصوفة للتو لتوسيع حيز الأرقام التسلسلية بفعالية بدلاً من تحديد حقل رقم تسلسلي جديد مؤلف من 64 بتًا. أي يقرر بروتوكول TCP قبول أو رفض جزء بناءً على معرّف بطول 64 بت يحتوي على حقل SequenceNum بترتيب 32 بت المنخفض والعلامة الزمنية بترتيب 32 بت العالي. بما أن العلامة الزمنية تتزايد دائمًا، فإنها تعمل على التمييز بين تجسبدَين مختلفين لنفس الرقم التسلسلي. لاحظ استخدام العلامة الزمنية في هذا الإعداد فقط للحماية من الالتفاف، حيث لا يُتعامَل معها كجزء من الرقم التسلسلي لغرض طلب البيانات أو الإشعار بوصولها. تسمح الإضافة الثالثة لبروتوكول TCP بالإعلان عن نافذةٍ أكبر، مما يسمح له بملء الأنابيب ذات جداء (التأخير × حيز النطاق التراسلي) الأكبر والتي تتيحها الشبكات عالية السرعة. تتضمن هذه الإضافة خيارًا يحدد عامل توسيع scaling factor للنافذة المعلن عنها. أي يسمح هذا الخيار لطرفي TCP بالاتفاق على أن حقل AdvertisedWindow يحسب أجزاءًا أكبر (عدد وحدات البيانات ذات 16 بايت الممكن عدم الإقرار بها من قبل المرسل على سبيل المثال) بدلًا من تفسير الرقم الذي يظهر في حقل AdvertisedWindow على أنه يشير إلى عدد البايتات المسموح للمرسل بعدم الإقرار بها. يحدد خيار توسيع النافذة عدد البتات التي يجب على كل جانبٍ إزاحة حقل AdvertisedWindow بها لليسار قبل استخدام محتوياته لحساب نافذة فعالة. تسمح الإضافة الرابعة لبروتوكول TCP بتوفير إشعاره التراكمي cumulative acknowledgment مع الإشعارات الانتقائية selective acknowledgments لأية أجزاءٍ إضافية جرى استلامها ولكنها ليست متجاورة مع جميع الأجزاء المُستلمة مسبقًا، وهذا هو خيار الإشعار الانتقائي أو SACK. يستمر جهاز الاستقبال بالإقرار أو الإشعار عن الأجزاء بشكلٍ طبيعي عند استخدام خيار SACK، أي لا يتغير معنى حقل Acknowledge، ولكنه يستخدم أيضًا حقولًا اختيارية في الترويسة لإرسال إشعارات وصول أي كتلٍ إضافية من البيانات المستلمة. يتيح ذلك للمرسل إعادة إرسال الأجزاء المفقودة فقط وفقًا للإشعار الانتقائي. هناك استراتيجيتان مقبولتان فقط للمرسل بدون SACK. تستجيب الاستراتيجية المتشائمة pessimistic لانتهاء المهلة بإعادة إرسال الجزء المنتهية مهلته، وكذلك أي أجزاء سترسَل لاحقًا أيضًا، وتفترض الإستراتيجية المتشائمة الأسوأ والذي هو فقد كل تلك الأجزاء. عيب الاستراتيجية المتشائمة هو أنها قد تعيد إرسال الأجزاء المُستلمة بنجاح في المرة الأولى دون داعٍ. الإستراتيجية الأخرى هي الإستراتيجية المتفائلة optimistic، والتي تستجيب لانتهاء المهلة بإعادة إرسال الجزء المنتهية مهلته فقط. يفترض النهج المتفائل السيناريو الأكثر تفاؤلًا والذي هو فقد جزء segment واحد فقط. عيب الإستراتيجية المتفائلة أنها بطيئة للغاية دون داعٍ، عندما تضيع سلسلة من الأجزاء المتتالية، كما يحدث عندما يكون هناك ازدحام. كما أنها بطيئة نظرًا لعدم القدرة على اكتشاف خسارة كل جزءٍ حتى يتلقى المرسل ACK لإعادة إرسال الجزء السابق، لذلك فهي تستهلك وقت RTT واحدًا لكل جزءٍ حتى يعيد إرسال جميع الأجزاء في السلسلة المفقودة. تتوفر استراتيجية أفضل للمرسل مع خيار SACK وهي: أعد إرسال الأجزاء التي تملأ الفجوات بين الأجزاء التي جرى الإقرار بها انتقائيًا. بالمناسبة، لا تمثّل هذه الإضافات القصة الكاملة. حيث سنرى بعض الإضافات الأخرى عندما ننظر في كيفية تعامل بروتوكول TCP مع الازدحام. تتعقب هيئة تخصيص أرقام الإنترنت Internet Assigned Numbers Authority أو اختصارًا IANA جميع الخيارات المحددة لبروتوكول TCP (إضافةً للعديد من بروتوكولات الإنترنت الأخرى). الأداء Performance تذكر أن في بداية السلسلة قد قدّم المقياسين الكميّين اللذين يجري من خلالِهما تقييم أداء الشبكة وهما: زمن الاستجابة latency والإنتاجية throughput. لا تتأثر هذه المقاييس فقط بالعتاد الأساسي، مثل تأخير الانتشار propagation delay وحيز نطاق الرابط التراسلي link bandwidth، كما هو مذكور في تلك المناقشة، ولكن تتأثر أيضًا بتكاليف البرمجيات الزائدة. يمكننا مناقشة كيفية قياس أداء البروتوكول القائم على البرمجيات بشكل هادف الآن بعد أن أصبح متاحًا لنا رسمًا بيانيًا كاملًا له متضمنًا بروتوكولات نقل بديلة. تكمن أهمية هذه القياسات في أنها تمثل الأداء الذي تراه برامج التطبيق. نبدأ، كما ينبغي لأي تقرير لنتائج التجارب، بوصف طريقتنا التجريبية. وهذا يشمل الجهاز المستخدم في التجارب، حيث تحتوي كل محطة عمل على زوج من معالجات Xeon لوحدة المعالجة المركزية بتردد 2.4 جيجاهرتز والتي تعمل بنظام لينوكس في هذه الحالة. يجري استخدام زوج من محوّلات إيثرنت، المسمَّى NIC بطاقة واجهة الشبكة network interface card، على كل جهاز لتمكين سرعات أعلى من 1 جيجابت في الثانية. يمتد الإيثرنت على غرفة جهاز واحدة لذا لا يمثل الانتشار مشكلة، مما يجعل هذا مقياسًا لتكاليف المعالجات / البرمجيات الزائدة. يحاول برنامجُ الاختبار الذي يعمل أعلى واجهة المقبس ببساطة نقلَ البيانات بأسرع ما يمكن من جهازٍ إلى آخر، ويوضح الشكل السابق هذا الإعداد. قد تلاحظ أن هذا الإعداد التجريبي لا يمثل ميزة خاصة من حيث العتاد أو سرعة الروابط. لا يتمثل الهدف من هذا القسم في إظهار مدى سرعة تشغيل بروتوكول معين، ولكن لتوضيح المنهجية العامة لقياس أداء البروتوكول والإبلاغ عنه. يجري اختبار الإنتاجية لمجموعة متنوعة من أحجام الرسائل باستخدام أداة قياس معيارية تسمى TTCP. يوضح الشكل التالي نتائج اختبار الإنتاجية. الشيء الرئيسي الواجب ملاحظته في هذا الرسم البياني هو أن معدل النقل يتحسن مع زيادة حجم الرسائل. وهذا أمرٌ منطقي، حيث تتضمن كل رسالة قدرًا معينًا من الحِمل الزائد، لذا فإن الرسالة الأكبر تعني أن هذا الحِمل يُستهلَك على عددٍ أكبر من البايتات. يُسطَّح منحني سرعة النقل الأعلى من 1 كيلوبايت، وعند هذه النقطة يصبح الحِمل لكل رسالة غير مهم عند مقارنته بالعدد الكبير من البايتات التي يتعين على مكدّس البروتوكول معالجتها. تجدر الإشارة إلى أن الحد الأقصى للإنتاجية أقل من 2 جيجابت في الثانية، وهي سرعة الرابط المتاحة في هذا الإعداد. ستكون هناك حاجة لمزيد من الاختبار وتحليل النتائج لمعرفة مكان الاختناق أو معرفة إذا كان هناك أكثر من عنق زجاجة أو اختناق bottleneck. قد يعطي النظر إلى حِمل وحدة المعالجة المركزية مؤشرًا على ما إذا كانت وحدة المعالجة المركزية هي مكان الاختناق أم أن اللوم يقع على حيز النطاق التراسلي للذاكرة أو أداء محوّل adaptor الشبكة أو بعض المشكلات الأخرى. نلاحظ أيضًا أن الشبكة في هذا الاختبار "مثالية" بصورةٍ أساسية. لا يوجد أي تأخير أو خسارة تقريبًا، وبالتالي فإن العوامل الوحيدة التي تؤثر على الأداء هي تطبيق بروتوكول TCP إضافةً إلى عتاد وبرمجيات محطة العمل. ولكن نتعامل في معظم الأوقات مع شبكاتٍ بعيدة عن الكمال، ولا سيما روابطنا المقيدة بحيّز النطاق التراسلي وروابط الميل الأخير والروابط اللاسلكية المعرّضة للضياع. نحتاج إلى فهم كيفية تعامل بروتوكول TCP مع الازدحام قبل أن نقّدر تمامًا كيف تؤثر هذه الروابط على أداء TCP. هدّدت سرعة روابط الشبكة المتزايدة بشكل مطرد بالتقدم على ما يمكن تسليمه إلى التطبيقات في أوقات مختلفة من تاريخ الشبكات. بدأ جهد بحثي كبير في الولايات المتحدة في عام 1989 لبناء "شبكات جيجابت gigabit networks" على سبيل المثال، حيث لم يكن الهدف فقط بناء روابط ومبدلات يمكن تشغيلها بسرعة 1 جيجابت في الثانية أو أعلى، ولكن أيضًا لإيصال هذه الإنتاجية على طول الطريق إلى عملية تطبيقٍ واحدة. كانت هناك بعض المشكلات الحقيقية (كان لابد من تصميم محولات شبكة، وبنيات محطات عمل، وأنظمة تشغيل مع مراعاة إنتاجية النقل من شبكة إلى تطبيق على سبيل المثال) وكذلك بعض المشكلات المُدرَكة التي تبين أنها ليست خطيرة للغاية. وكان على رأس قائمة مثل هذه المشاكل القلقُ من أن بروتوكولات النقل الحالية، وخاصةً بروتوكول TCP، قد لا تكون على مستوى التحدي المتمثل في عمليات الجيجابت. كما اتضح، كان أداء بروتوكول TCP جيدًا في مواكبة الطلبات المتزايدة للشبكات والتطبيقات عالية السرعة. أحد أهم العوامل هو إدخال توسيع النافذة للتعامل مع منتجات تأخير حيز النطاق التراسلي الأكبر، ولكن غالبًا ما يكون هناك فرقٌ كبير بين الأداء النظري لبروتوكول TCP وما يجري تحقيقه عمليًا. يمكن أن تؤدي المشكلات البسيطة نسبيًا مثل نسخ البيانات مرات أكثر من اللازم أثناء انتقالها من محول الشبكة إلى التطبيق إلى انخفاض الأداء، كما يمكن أن تؤدي ذاكرة التخزين المؤقت غير الكافية إلى انخفاض الأداء عندما يكون منتج تأخير حيز النطاق التراسلي كبيرًا. وديناميكيات TCP معقدة بدرجة كافية، بحيث يمكن للتفاعلات الدقيقة بين سلوك الشبكة وسلوك التطبيق وبروتوكول TCP نفسه تغيير الأداء بصورةٍ كبيرة. من الجدير بالذكر أن بروتوكول TCP يستمر في الأداء الجيد مع زيادة سرعات الشبكة، ويندفع الباحثون لإيجاد حلول عند حدوث تعارض مع بعض القيود (المرتبطة عادةً بالازدحام أو زيادة منتجات تأخير حيز النطاق التراسلي أو كليهما). خيارات التصميم البديلة SCTP وQUIC على الرغم من أن بروتوكول TCP قد أثبت أنه بروتوكول قوي يلبي احتياجات مجموعة واسعة من التطبيقات، إلا أن مساحة تصميم بروتوكولات النقل كبيرة جدًا. ليس بروتوكول TCP بأي حالٍ النقطة الصالحة الوحيدة في مساحة التصميم هذه. نختم مناقشتنا لبروتوكول TCP من خلال النظر في خيارات التصميم البديلة. بينما نقدم شرحًا حول سبب اتخاذ مصممي TCP الخيارات التي قاموا بها، فإننا نلاحظ وجود بروتوكولات أخرى اتخذت خيارات أخرى، وقد يظهر المزيد من هذه البروتوكولات في المستقبل. هناك صنفان على الأقل من بروتوكولات النقل: البروتوكولات الموجَّهة بالتدفق stream-oriented protocols مثل بروتوكول TCP وبروتوكولات الطلب / الرد request/reply protocols مثل بروتوكول RPC. قسمنا مساحة التصميم ضمنيًا إلى نصفين ووضعنا بروتوكول TCP مباشرةً في نصف عالم البروتوكولات الموجَّهة بالتدفق. يمكننا أيضًا تقسيم البروتوكولات الموجهة بالتدفق إلى مجموعتين، موثوقة reliable وغير موثوقة unreliable، مع احتواء الأولى على بروتوكول TCP والأخيرة أكثر ملاءمة لتطبيقات الفيديو التفاعلية التي تفضل إسقاط أو إهمال إطار بدلًا من تحمّل التأخير المرتبط بإعادة الإرسال. يُعَد بناء تصنيف لبروتوكول النقل أمرًا مثيرًا للاهتمام ويمكن مواصلته بتفاصيل أكثر، ولكن العالم ليس أبيض وأسود كما قد نحب. افترض ملاءمة بروتوكول TCP كبروتوكول نقل لتطبيقات الطلب / الرد على سبيل المثال. بروتوكول TCP هو بروتوكول ثنائي الاتجاه، لذلك سيكون من السهل فتح اتصال TCP بين العميل والخادم، وإرسال رسالة الطلب في اتجاه واحد، وإرسال رسالة الرد في الاتجاه الآخر، ولكن هناك نوعان من التعقيدات. الأول هو أن بروتوكول TCP هو بروتوكول موجَّهٌ بالبايت وليس بروتوكول موجَّه بالرسائل، وأن تطبيقات الطلب / الرد تتعامل دائمًا مع الرسائل. (سنكتشف مسألة البايتات مقابل الرسائل بتفصيل أكبر بعد قليل). التعقيد الثاني هو أنه في تلك المواقف التي تتلاءم فيها كل من رسالة الطلب ورسالة الرد في رزمة شبكة واحدة، يحتاج بروتوكول الطلب / الرد المصمم جيدًا رزمتان فقط لتطبيق التبادل، بينما يحتاج بروتوكول TCP إلى تسع رزم على الأقل: ثلاث لتأسيس الاتصال، واثنان لتبادل الرسائل، وأربع لإنهاء الاتصال. إذا كانت رسائل الطلب أو الرد كبيرة بما يكفي لأن تتطلب رزم شبكةٍ متعددة (قد يستغرق الأمر 100 رزمة لإرسال إستجابة بحجم 100000 بايت على سبيل المثال)، ستكون التكاليف الزائدة لإعداد الاتصال وإلغائه غير منطقية. ليس الأمر دائمًا أن بروتوكولًا معينًا لا يمكنه دعم وظيفةٍ معينة، ففي بعض الأحيان يكون هناك تصميم أكثر كفاءة من تصميم آخر في ظل ظروف معينة. ثانيًا، قد تتساءل عن سبب اختيار TCP لتقديم خدمة تدفق بايتات موثوقة بدلًا من خدمة تدفق رسائل موثوقة، حيث تُعتبر الرسائل الخيار الطبيعي لتطبيق قاعدة البيانات الذي يريد تبادل السجلات. هناك إجابتان على هذا السؤال: الأول هو أن البروتوكول الموجَّه بالرسائل يجب أن ينشئ حدًا أعلى لأحجام الرسائل، فالرسالة الطويلة التي بلا حدود هي تدفق بايتات. ستكون هناك تطبيقات تريد إرسال رسائل أكبر بالنسبة إلى أي حجم رسالة يحدده البروتوكول، مما يجعل بروتوكول النقل عديم الفائدة ويجبر التطبيق على تنفيذ خدماته الشبيهة بالنقل. السبب الثاني هو أنه على الرغم من أن البروتوكولات الموجهة بالرسائل هي بالتأكيد أكثر ملاءمة للتطبيقات التي ترغب في إرسال السجلات إلى بعضها البعض، إلا أنه يمكنك بسهولة إدخال حدود السجل في تدفق البايتات لتطبيق هذه الوظيفة. القرار الثالث الذي اُتخِذ في تصميم TCP هو أنه يسلّم البايتات بناءً على التطبيق. هذا يعني أنه قد يحتفظ بالبايتات التي استلمها مخالفةً للترتيب من الشبكة، في انتظار بعض البايتات المفقودة لملء ثغرة. هذا مفيد للغاية للعديد من التطبيقات ولكن تبين أنه غير مفيد تمامًا إذا كان التطبيق قادرًا على معالجة البيانات المخالفة للترتيب. فلا تحتاج صفحة الويب مثلًا التي تحتوي على كائنات متعددة مضمَّنة إلى تسليم جميع الكائنات بالترتيب قبل البدء في عرض الصفحة. هناك صنف من التطبيقات يفضِّل التعامل مع البيانات المخالفة للترتيب في طبقة التطبيق، مقابل الحصول على البيانات في وقت أقرب عند إسقاط الرزم أو سوء ترتيبها داخل الشبكة. أدت الرغبة في دعم مثل هذه التطبيقات إلى إنشاء بروتوكولي نقل معياريين من IETF. كان أولها بروتوكول SCTP، بروتوكول نقل التحكم في التدفق Stream Control Transmission Protocol. يوفر بروتوكول SCTP خدمة توصيل مُرتَّبة جزئيًا، بدلًا من خدمة TCP المُرتَّبة بدقة. (يتخذ بروتوكول SCTP أيضًا بعض قرارات التصميم الأخرى التي تختلف عن بروتوكول TCP، بما في ذلك اتجاه الرسالة ودعم عناوين IP المتعددة لجلسة واحدة). وحّدت منظمة IETF في الآونة الأخيرة بروتوكولًا محسَّنًا لحركة مرور الويب، يُعرف باسم QUIC. رابعًا، اختار بروتوكول TCP تطبيق مراحل إعداد / تفكيك صريحة، لكن هذا ليس مطلوبًا، حيث سيكون من الممكن إرسال جميع معاملات الاتصال الضرورية مع رسالة البيانات الأولى في حالة إعداد الاتصال. اختار TCP اتباع نهجٍ أكثر تحفظًا يمنح المتلقي الفرصة لرفض الاتصال قبل وصول أي بيانات. يمكننا إغلاق الاتصال الذي كان غير نشط لفترة طويلة من الزمن بهدوء في حالة التفكيك، ولكن هذا من شأنه أن يعقّد تطبيقات مثل تسجيل الدخول عن بُعد الذي يريد الحفاظ على الاتصال نشطًا لأسابيع في كل مرة، حيث ستُجبَر هذه التطبيقات على إرسال رسائل "keep alive" خارج النطاق للحفاظ على حالة الاتصال عند الطرف الآخر من الاختفاء. أخيرًا، بروتوكول TCP هو بروتوكول قائم على النافذة window-based، لكن هذا ليس الاحتمال الوحيد. البديل هو التصميم القائم على المعدَّل rate-based design، حيث يخبر المستلمُ المرسلَ بالمعدّل (معبرًا عنه إما بالبايتات أو بالرزم في الثانية) الذي يكون على استعداد لقبول البيانات الواردة إليه، فقد يخبر المتلقي المرسل على سبيل المثال أنه يستطيع استيعاب 100 رزمة في الثانية. هناك ازدواجية مثيرة للاهتمام بين النوافذ والمعدَّل، حيث أن عدد الرزم (البايتات) في النافذة، مقسومًا على RTT، هو المعدل بالضبط. يشير حجم النافذة المكوَّن من 10 رزم و 100 ميلي ثانية RTT على سبيل المثال إلى أنه يُسمح للمرسل بالإرسال بمعدل 100 رزمة في الثانية. يرفع جهاز الاستقبال أو يخفض المعدل الذي يمكن للمرسل الإرسال به بفعالية من خلال زيادة أو تقليل حجم النافذة المعلن عنها. تُرَد هذه المعلومات مرةً أخرى إلى المرسل في حقل AdvertisedWindow من إشعار ACK كل جزء في بروتوكول TCP. تتمثل إحدى المشكلات الرئيسية في البروتوكول المستند إلى المعدل في عدد المرات التي يُرحَّل فيها المعدل المطلوب (والذي قد يتغير بمرور الوقت) إلى المصدر: هل هو لكل رزمة أم مرة واحدة لكل RTT أم عندما يتغير المعدل فقط؟ على الرغم من أننا قد ناقشنا للتو النافذة مقابل المعدل في سياق التحكم في التدفق، إلا أنها قضية متنازع عليها بشدة في سياق التحكم في الازدحام، والتي ستُناقش لاحقًا. بروتوكول QUIC أُنشِئ بروتوكول اتصالات إنترنت بروتوكول UDP السريعة Quick UDP Internet Connections أواختصارًا QUIC في Google في عام 2012، ولا يزال يخضع للتوحيد القياسي standardization في منظمة IETF في وقت كتابة هذا الكتاب، ولقد شهد بالفعل قدرًا معتدلًا من النشر (في بعض متصفحات الويب وعدد كبير من مواقع الويب الشائعة). حقيقة أنه كان ناجحًا إلى هذه الدرجة هي في حد ذاتها جزءٌ مثير للاهتمام من قصة QUIC، وكانت قابلية النشر التزامًا رئيسيًا لمصممي البروتوكول. يأتي الدافع وراء بروتوكول QUIC مباشرةً من النقاط التي أشرنا إليها أعلاه حول بروتوكول TCP: لقد تبين أن بعض قرارات التصميم غير مثالية لمجموعة من التطبيقات التي تعمل عبر بروتوكول TCP، كحركة مرور بروتوكول HTTP (الويب). أصبحت هذه المشكلات أوضح بمرور الوقت، نظرًا لعوامل مثل ظهور الشبكات اللاسلكية ذات زمن الاستجابة العالي، وتوافر شبكات متعددة لجهاز واحد (شبكة Wi-Fi والشبكة الخلوية على سبيل المثال)، والاستخدام المتزايد للتشفير واستيثاق الاتصالات على الويب. تستحق بعض قرارات تصميم بروتوكول QUIC الرئيسية المناقشة في حين أن وصفه الكامل خارج نطاقنا. إذا كان وقت استجابة الشبكة مرتفعًا (من رتبة مئات الميلي ثانية) فيمكن أن تزيد بسرعة بعض فترات RTT إزعاجًا واضحًا للمستخدم النهائي. يستغرق عادةً إنشاء جلسة HTTP عبر بروتوكول TCP مع تأمين طبقة النقل ثلاث رحلاتٍ ذهابًا وإيابًا (واحدة لتأسيس جلسة TCP واثنتان لإعداد معاملات التشفير) قبل إرسال رسالة HTTP الأولى. أدرك مصممو QUIC أنه يمكن تقليل هذا التأخير (وهو النتيجة المباشرة لنهج متعدد الطبقات لتصميم البروتوكول) بصورةٍ كبيرة إذا دُمِج إعداد الاتصال ومصافحات الأمان المطلوبة وحُسِّن لأدنى حد من وقت الذهاب والإياب. لاحظ أيضًا كيف قد يؤثر وجود واجهات متعددة للشبكة على التصميم. إذا فقد هاتفك المحمول اتصال Wi-Fi وتحتاج إلى التبديل إلى اتصال خلوي، سيتطلب ذلك عادةً مهلة TCP على اتصالٍ واحد وسلسلة جديدة من المصافحات على الاتصال الآخر. كان جعل الاتصال شيئًا يمكنه الاستمرار عبر اتصالات طبقة الشبكة المختلفة هدفًا تصميميًا آخر لبروتوكول QUIC. أخيرًا، يُعد نموذج تدفق البايتات الموثوق لبروتوكول TCP تطابقًا ضعيفًا مع طلب صفحة الويب، عند الحاجة إلى جلب العديد من الكائنات ويمكن البدء بعرض الصفحة قبل وصولها جميعًا. أحد الحلول لذلك هو فتح اتصالات TCP متعددة على التوازي، ولكن هذا الأسلوب (الذي اُستخدِم في الأيام الأولى للويب) له مجموعة من العيوب الخاصة به، لا سيما فيما يتعلق بالتحكم في الازدحام. ومن المثير للاهتمام أنه جرى اتخاذ العديد من قرارات التصميم التي شكّلت تحدياتٍ لنشر بروتوكول نقل جديد بحلول الوقت الذي ظهر فيه QUIC. والجدير بالذكر أن العديد من "الصناديق المتوسطة middleboxes" مثل الجدران النارية firewalls وNAT لديها فهم كافٍ لبروتوكولات النقل المنتشرة على نطاق واسع (TCP وUDP) بحيث لا يمكن الاعتماد عليها لتمرير بروتوكول نقل جديد. ونتيجة لذلك يتواجد بروتوكول QUIC في الواقع فوق بروتوكول UDP، أي أنه بروتوكول نقلٍ يعمل فوق بروتوكول نقل. يطبّق بروتوكول QUIC إنشاء اتصالٍ سريع مع التشفير والاستيثاق authentication في أول RTT، ويفضّل توفير معرّف اتصال على استمراره عبر التغييرات في الشبكة الأساسية. وهو يدعم تعدد إرسال عدة تدفقات على اتصال نقل واحد، لتجنب توقف رأس الخط الذي قد ينشأ عند إسقاط رزمةٍ واحدة بينما يستمر وصول البيانات المفيدة الأخرى. ويحافظ على خصائص تجنب الازدحام لبروتوكول TCP. بروتوكول QUIC هو التطور الأكثر إثارة للاهتمام في عالم بروتوكولات النقل. العديد من قيود بروتوكول TCP معروفةٌ منذ عقود، ولكن يمثّل بروتوكول QUIC واحدًا من أنحج الجهود حتى الآن للتركيز على نقطة مختلفة في مساحة التصميم. ويقدم دراسة حالة رائعة في النتائج غير المتوقعة للتصميمات متعددة الطبقات وفي تطور الإنترنت، نظرًا لأنه مستوحى من تجربة بروتوكول HTTP والويب، والتي نشأت بعد فترة طويلة من تأسيس بروتوكول TCP بشكل جيد في الإنترنت. بروتوكول TCP متعدد المسارات Multipath TCP ليس من الضروري دائمًا تحديد بروتوكول جديد إذا وجدت أن البروتوكول الحالي لا يخدم حالة استخدام معينة بشكل كافٍ. من الممكن في بعض الأحيان إجراء تغييرات جوهرية في كيفية تطبيق بروتوكول موجود، ولكن تظل وفية للمواصفات الأصلية. يعد بروتوكول Multipath TCP مثالًا على مثل هذه الحالة. تتمثل فكرة Multipath TCP في توجيه الرزم خلال مسارات متعددة عبر الإنترنت، باستخدام عنوانَي IP مختلفين لإحدى نقاط النهاية على سبيل المثال. يمكن أن يكون هذا مفيدًا بشكل خاص عند تسليم البيانات إلى جهازٍ محمول متصل بكل من شبكة Wi-Fi والشبكة الخلوية (وبالتالي يملك عنوانين IP فريدين). يمكن أن تواجه فقدانًا كبيرًا في الرزمة نظرًا لكون كلتا الشبكتين لاسلكيتين، لذا فإن القدرة على استخدامهما لحمل الرزم يمكن أن تحسّن تجربة المستخدم بصورة كبيرة. الحل هو أن يعيد الجانب المستلم من TCP بناء تدفق البايتات الأصلي بالترتيب قبل تمرير البيانات إلى التطبيق، والذي يظل غير مدرك أنه يجلس على بروتوكول Multipath TCP. (وهذا على عكس التطبيقات التي تفتح عمدًا اتصالين أو أكثر من اتصالات TCP للحصول على أداء أفضل). يبدو بروتوكول Multipath TCP بسيطًا، ولكنه من الصعب للغاية الحصول عليه بصورةٍ صحيحة لأنه يكسر العديد من الافتراضات حول كيفية تطبيق التحكم في تدفق TCP، وإعادة تجميع الجزء segment بالترتيب، والتحكم في الازدحام. نتركه كتمرينٍ لك لاستكشاف التفاصيل الدقيقة، حيث يُعد القيام بذلك طريقةً رائعة للتأكد من أن فهمك الأساسي لبروتوكول TCP سليم. ترجمة -وبتصرّف- للقسم Reliable Byte Stream من فصل ProtocolsEnd-to-End من كتاب Computer Networks: A Systems Approach. اقرأ أيضًا بروتوكولات طرف إلى طرف End-to-End Protocols في الشبكات الحاسوبيةالمقال السابق: تأسيس الشبكات الحاسوبية والتعرف على تطبيقاتها البرمجيات المستخدمة في بناء الشبكات الحاسوبية التوجيه Routing بين الأجهزة المتنقلة في الشبكات الحاسوبية دليل بصري لكيفية استخدام أنفاق SSH
  4. سنتحدث في هذا المقال مشكلة تواصل العمليات في البداية، والدور الذي يلعبه مستوى النقل transport في معمارية الشبكة، والذي يُسمى أحيانًا بروتوكول طرفٍ إلى طرف end-to-end، بعدها سنعرض الخدمات التي تحقق هذا الدور، مع شرح خدمة فك تعدد إرسال demultiplexing بسيطة من بين هذه الخدمات باستخدام بروتوكول UDP مشكلة تواصل العمليات Getting Processes to Communicate يمكن استخدام العديد من التقنيات لتوصيل مجموعة حواسيبٍ معًا، بدءًا من شبكات الإيثرنت البسيطة، والشبكات اللاسلكية، وحتى الشبكات المتشابكة ذات النطاق العالمي. وبمجرد الترابط، فستكون المشكلة التالية هي تحويل خدمة تسليم الرزمة من مضيف إلى مضيف إلى قناة اتصال بنمط عملية إلى عملية. هذا هو الدور الذي يلعبه مستوى النقل transport في معمارية الشبكة، والذي يُسمى أحيانًا بروتوكول طرفٍ إلى طرف end-to-end لأنه يدعم الاتصال بين برامج التطبيق التي تعمل في العقد النهائية. تتجسد قوة بروتوكول طرف إلى طرف بعاملين مهمين: الأول من جهة الطبقات الأعلى، حيث أن عمليات مستوى طبقة التطبيق التي تستخدم خدمات مستوى طبقة النقل لها متطلباتٌ معينة. تحدّد القائمة التالية بعض الخصائص العامة المُتوقَّع توفيرها ببروتوكول النقل: ضمان تسليم الرسالة. تسليم الرسائل بنفس ترتيب إرسالها. تسليم نسخةً واحدة على الأكثر من كل رسالة. دعم الرسائل الكبيرة العشوائية. دعم التزامن بين المرسل والمستقبل. السماح للمستلم بتطبيق التحكم في التدفق على المرسل. دعم عمليات تطبيق متعددة على كل مضيف. لاحظ أن هذه القائمة لا تتضمن جميع الوظائف التي قد تحتاجها عمليات التطبيق من الشبكة، فلا تتضمن ميزات الأمن مثل الاستيثاق أو التشفير والتي توّفرها عادةً بروتوكولاتٌ تقع فوق مستوى النقل. (سنناقش الموضوعات المتعلقة بالأمن لاحقًا). أما العامل الثاني فهو يتعلق بالطبقات السفلى، حيث أن الشبكة الأساسية التي يعمل عليها بروتوكول النقل لها قيودٌ معينة في مستوى الخدمة التي يمكن أن يوفرها. تتمثل بعض القيود الأكثر شيوعًا للشبكة في امكانية: إسقاط الرسائل. إعادة ترتيب الرسائل. تسليم نسخ مكررة من رسالة معينة. تقييد حجم الرسائل بحجمٍ محدود. تسليم الرسائل بعد تأخير طويل وعشوائي. ويقال أن مثل هذه الشبكة توفر أفضل جهدٍ best-effort من مستوى الخدمة، كما يتضح من الإنترنت. وبالتالي، يكمن التحدي في تطوير الخوارزميات التي ترقّي الخصائص الأقل من الخصائص المثالية للشبكة الأساسية إلى مستوى عالٍ من الخدمة التي تتطلبها برامج التطبيق. تستخدم بروتوكولات النقل المختلفة مجموعات مختلفة من هذه الخوارزميات. يبحث هذا المقال في هذه الخوارزميات في سياق أربع خدمات تمثيلية هي: خدمة فك تعدد إرسال demultiplexing بسيطة غير متزامنة وخدمة تدفق بايتٍ موثوقة و"خدمة طلب / رد request/reply" وخدمة تطبيقات الزمن الحقيقي. نستخدم، في حالة خدمات فك تعدد الإرسال وتدفق البايت، بروتوكول مخطط بيانات المستخدم User Datagram Protocol أو اختصارًا UDP وبروتوكول التحكم في الإرسال Transmission Control Protocol أو اختصارًا TCP على التوالي لتوضيح كيفية تقديم هذه الخدمات عمليًا. بينما نناقش، في حالة خدمة الطلب / الرد، الدور الذي تلعبه في خدمة استدعاء الإجراءات البعيدة Remote Procedure Call أو اختصارًا RPC وميزاتها. لا يحتوي الإنترنت على بروتوكول RPC واحد، لذلك قمنا بتغطية هذه المناقشة بوصفٍ لثلاثة من بروتوكولات RPC المستخدمة على نطاق واسع: SunRPC وDCE-RPC وgRPC. أخيرًا، تفرض تطبيقات الزمن الحقيقي متطلباتٍ خاصة على بروتوكول النقل، مثل الحاجة إلى نقل معلومات التوقيت التي تسمح بإعادة تشغيل عينات الصوت أو الفيديو في النقطة الزمنية المناسبة. سيتم التركيز على المتطلبات التي وضعتها التطبيقات على مثل هذا البروتوكول من خلال أكثر الأمثلة استخدامًا وهو بروتوكول النقل في الوقت الحقيقي Real-Time Transport Protocol أو اختصارًا RTP. فك تعدد الإرسال البسيط Simple Demultiplexor باستخدام بروتوكول UDP إن أبسط بروتوكول نقل ممكن هو الذي يوسّع خدمة التوصيل من مضيف إلى مضيف للشبكة الأساسية إلى خدمة اتصال عمليةٍ إلى عملية. من المحتمل أن يكون هناك العديد من العمليات التي تعمل على أي مضيفٍ معين، لذلك يحتاج البروتوكول إلى إضافة مستوىً من فك تعدد الإرسال، مما يسمح لعمليات تطبيقٍ متعددة على كل مضيف بمشاركة الشبكة. لا يضيف بروتوكول النقل أي وظيفة أخرى لخدمة أفضل جهد التي تقدمها الشبكة الأساسية بصرف النظر عن هذا المطلب. يُعد بروتوكول مخطط بيانات المستخدم UDP للإنترنت مثالًا عن بروتوكول النقل هذا. المشكلة الوحيدة المثيرة للاهتمام في مثل هذا البروتوكول هي شكل العنوان المستخدم لتحديد العملية المستهدفة. على الرغم من أنه من الممكن للعمليات تحديد بعضها بعضًا مباشرةً باستخدام معرّف العملية process id أو اختصارًا pid المخصّص لنظام التشغيل، فإن مثل هذا النهج عمليٌّ فقط في نظامٍ موزَّع مغلَقٍ يعمل فيه نظام تشغيل واحد على جميع المضيفين ويسند لكل عمليةٍ معرّفًا فريدًا. الأسلوب الأكثر شيوعًا، والذي يستخدمه بروتوكول UDP، هو للعمليات التي تحدّد بعضها بعضًا بطريقةٍ غير مباشرة باستخدام محددٍ موقع مجرد abstract locator، يسمى عادةً منفذًا port. الفكرة الأساسية هي أن ترسل عملية المصدر رسالة إلى منفذ وأن تتلقّى عملية الوجهة الرسالة من منفذ. تحتوي ترويسة بروتوكول طرفٍ إلى طرف الذي يطبّق وظيفة فك تعدد الإرسال هذه على معرّفٍ (منفذ) لكل من مرسل (مصدر) ومُستقبِل (وجهة) للرسالة. يوضح الشكل التالي ترويسة UDP على سبيل المثال. لاحظ أن حقل منفذ UDP يبلغ طوله 16 بتًا فقط، وهذا يعني أن هناك ما يصل إلى 64 ألفًا من المنافذ المحتملة، ومن الواضح أنها غير كافية لتحديد جميع العمليات على جميع الأجهزة المضيفة في الإنترنت. لحسن الحظ، لا تُفسَّر المنافذ عبر الإنترنت بالكامل، ولكن فقط على مضيفٍ واحد، أي يحدّد منفذٌ على مضيفٍ معين العملية فعليًا زوج "منفذ ومضيف". يشكّل هذا الزوج مفتاح فك تعدد الإرسال لبروتوكول UDP. المشكلة التالية هي كيفية تعرّف العملية على منفذ العملية التي تريد إرسال رسالة إليها. تبدأ عادةً عملية العميل في تبادل الرسائل مع عملية الخادم، وبمجرد اتصال العميل بالخادم، يعرف الخادم منفذ العميل (من حقل SrcPrt المضمَّن في ترويسة الرسالة) ويمكنه الرد عليه. وبالتالي فإن المشكلة الحقيقية هي بكيفية تعرّف العميل على منفذ الخادم في المقام الأول. من الأساليب الشائعة أن يقبل الخادم الرسائل على منفذٍ معروف جيدًا، أي يتلقّى كل خادم رسائله على منفذٍ ثابت منشورٍ على نطاقٍ واسع، مثل خدمة هاتف الطوارئ المتوفرة في الولايات المتحدة على رقم الهاتف المعروف 911. يتلقى خادم اسم النطاق Domain Name Server أو اختصارًا DNS في الإنترنت، على سبيل المثال، الرسائل على المنفذ المعروف جيدًا (53) على كل مضيف، وتستمع خدمة البريد للرسائل على المنفذ (25)، ويقبل برنامج يونيكس talk الرسائلَ على المنفذ المعروف (517)، وغير ذلك. يُنشَر هذا الربط mapping دوريًا في RFC وهو متاح في معظم أنظمة يونيكس في الملف /etc/services. يكون المنفذ المعروف في بعض الأحيان مجردَ نقطة بداية الاتصال: يستخدم العميل والخادم المنفذَ المعروف جيدًا للاتفاق على منفذٍ آخر سيستخدمانه في الاتصالات اللاحقة، مما يترك المنفذ المعروف متاحًا للعملاء الآخرين. تتمثل الإستراتيجية البديلة في تعميم هذه الفكرة، بحيث لا يوجد سوى منفذٍ واحد معروف جيدًا، وهو المنفذ الذي تقبل فيه خدمة رابط المنافذ port mapper الرسائل. وبالتالي يرسل العميل رسالةً إلى منفذ رابط المنافذ المعروف جيدًا طالبًا منه المنفذ الذي يجب أن يستخدمه للتحدث إلى "أية" خدمة، فيرجع رابطُ المنافذ المنفذ المناسب. تسهّل هذه الإستراتيجية تغيير المنفذ المرتبط بخدمات مختلفة بمرور الوقت وتسهّل لكل مضيفٍ استخدام منفذ مختلف لنفس الخدمة. إن المنفذ هو مجرد فكرةٍ مجردة كما ذكرنا للتو، وتختلف الطريقة التي تُطبَّق فيها بالضبط من نظامٍ إلى آخر، أو بصورةٍ أدق، من نظام تشغيل لآخر، وتُعد واجهة برمجة تطبيقات المقبس socket API مثالٌ على تطبيق المنافذ. يُطبَّق المنفذ عادةً بواسطة رتل رسائل، كما هو موضح في الشكل التالي. يلحق البروتوكول (بروتوكول UDP مثلًا) الرسالة بنهاية الرتل عند وصولها، وتُهمَل الرسالة في حالة امتلاء الرتل. لا توجد آلية للتحكم في التدفق في بروتوكول UDP لإخبار المرسل بإبطاء السرعة، حيث تُزَال رسالةٌ من مقدمة الرتل عندما تريد عملية التطبيق تلقي رسالة. وإذا كانت قائمة الانتظار فارغة، فستُوقَف العملية حتى تصبح الرسالة متاحة. أخيرًا، على الرغم من أن بروتوكول UDP لا يطبّق التحكم في التدفق أو التسليم الموثوق / المرتب، إلا أنه يوفر وظيفةً أخرى بخلاف فك تعدد إرسال الرسائل إلى بعض عمليات التطبيق، كما يضمَن صحة الرسالة باستخدام المجموع الاختباري checksum (يُعَد المجموع الاختباري لبروتوكول UDP اختياريًا في IPv4 ولكنه إلزامي في IPv6). خوارزمية المجموع الاختباري الأساسية لبروتوكول UDP هي نفسها المستخدمة في بروتوكول IP، أي أنها تضيف مجموعةً من الكلمات ذات 16 بتًا باستخدام حساب متمم الواحد ones’ complement وتحسب متمم الواحد للنتيجة. لكن بيانات الإدخال المستخدمة في المجموع الاختباري غير متوقعة بعض الشيء. يأخذ المجموع الاختباري لبروتوكول UDP ترويسة UDP ومحتويات جسم الرسالة وشيئًا يسمَّى الترويسة الزائفة pseudoheader كمدخلات. تتكون الترويسة الزائفة من ثلاثة حقولٍ من ترويسة IP، هي رقم البروتوكول protocol number وعنوان IP المصدر وعنوان IP الوجهة، بالإضافة إلى حقل طول UDP، حيث يُضمَّن حقل طول UDP مرتين في حساب المجموع الاختباري. يُعَد الدافع وراء وجود الترويسة الزائفة هو التحقق من تسليم هذه الرسالة بين نقطتي النهاية الصحيحتين. إذا عُدِّل عنوان IP الوجهة أثناء نقل الرزمة على سبيل المثال، مما يتسبب في خطأ بتسليم الرزمة، فستُكتشَف هذه الحقيقة بواسطة مجموع بروتوكول UDP الاختباري. ترجمة -وبتصرّف- للقسم Simple Demultiplexor من الفصل End-to-End Protocols من كتاب Computer Networks: A Systems Approach. اقرأ أيضًا المقال السابق: التوجيه Routing بين الأجهزة المتنقلة في الشبكات الحاسوبية كيف تختار سياسة فعالة للجدار الناري لتأمين خواديمك - الجزء الثاني التشبيك المتقدم Advanced Internetworking في الشبكات الحاسوبية التشبيك Internetworking بين أنواع مختلفة من الشبكات الحاسوبية
×
×
  • أضف...