سؤال

أعتقد أنني أفهم المعنى الرسمي للخيار. في بعض التعليمات البرمجية القديمة التي أتعامل معها الآن ، يتم استخدام الخيار. يشتكي العميل من RST كاستجابة لليهن من جانبها على اتصال قريب من جانبها.

لست متأكدًا من أنه يمكنني إزالته بأمان ، لأنني لا أفهم متى يجب استخدامه.

هل يمكنك تقديم مثال على متى سيكون الخيار مطلوبًا؟

هل كانت مفيدة؟

المحلول

السبب النموذجي لتعيين أ SO_LINGER مهلة الصفر هي تجنب أعداد كبيرة من الاتصالات التي تجلس في TIME_WAIT الدولة ، وربط جميع الموارد المتاحة على الخادم.

عندما يتم إغلاق اتصال TCP بشكل نظيف ، تنتهي النهاية التي بدأت الإغلاق ("الإغلاق النشط") بالاتصال جالسًا TIME_WAIT لعدة دقائق. لذلك إذا كان بروتوكولك هو واحد حيث الخادم يبدأ عن إغلاق الاتصال ، ويتضمن أعدادًا كبيرة جدًا من الاتصالات قصيرة العمر ، ثم قد يكون عرضة لهذه المشكلة.

هذه ليست فكرة جيدة ، رغم ذلك - TIME_WAIT موجود لسبب ما (لضمان أن الحزم الضالة من الاتصالات القديمة لا تتداخل مع اتصالات جديدة). إنها فكرة أفضل لإعادة تصميم بروتوكولك إلى بروتوكول حيث يبدأ العميل في إغلاق الاتصال ، إن أمكن.

نصائح أخرى

للحصول على اقتراحي ، يرجى قراءة القسم الأخير: "متى تستخدم so_linger مع timeout 0".

قبل أن نصل إلى ذلك محاضرة صغيرة حول:

  • إنهاء TCP الطبيعي
  • TIME_WAIT
  • FIN, ACK و RST

إنهاء TCP الطبيعي

يبدو تسلسل إنهاء TCP العادي مثل هذا (مبسط):

لديناانانان: أ و ب

  1. مكالمات close()
    • يرسل FIN إلى ب
    • يذهب إلى FIN_WAIT_1 حالة
  2. ب يستقبل FIN
    • ب يرسل ACK إلى
    • B يذهب إلى CLOSE_WAIT حالة
  3. يتلقى ACK
    • يذهب إلى FIN_WAIT_2 حالة
  4. ن مكالمات close()
    • ب يرسل FIN إلى
    • B يذهب إلى LAST_ACK حالة
  5. يتلقى FIN
    • يرسل ACK إلى ب
    • يذهب إلى TIME_WAIT حالة
  6. ب يستقبل ACK
    • B يذهب إلى CLOSED الحالة - تتم إزالة IE من جداول المقبس

وقت الانتظار

لذا فإن النظير الذي يبدأ الإنهاء - أي مكالمات close() أولا - سينتهي به المطاف في TIME_WAIT حالة.

لفهم لماذا TIME_WAIT الحالة هي صديقنا ، يرجى قراءة القسم 2.7 في "Unix Network Programming" الإصدار الثالث من Stevens et al (صفحة 43).

ومع ذلك ، يمكن أن تكون مشكلة في الكثير من المآخذ في TIME_WAIT قم بالدولة على خادم لأنه قد يمنع قبول الاتصالات الجديدة في النهاية.

للتغلب على هذه المشكلة ، رأيت الكثير يقترحون تعيين خيار SO_Linger Socket مع Timeout 0 قبل الاتصال close(). ومع ذلك ، هذا حل سيء لأنه يتسبب في إنهاء اتصال TCP بخطأ.

بدلاً من ذلك ، صمم بروتوكول التطبيق الخاص بك بحيث يتم تشغيل إنهاء الاتصال دائمًا من جانب العميل. إذا كان العميل يعرف دائمًا متى يقرأ جميع البيانات المتبقية ، فيمكنه بدء تسلسل الإنهاء. على سبيل المثال ، يعرف المتصفح من Content-Length HTTP Header عند قراءة جميع البيانات ويمكنه بدء الإغلاق. (أعلم أنه في HTTP 1.1 سيبقيه مفتوحًا لفترة من الوقت لإعادة استخدامه المحتمل ، ثم أغلقه.)

إذا احتاج الخادم إلى إغلاق الاتصال ، فصمم بروتوكول التطبيق حتى يطلب الخادم من العميل الاتصال close().

متى تستخدم so_linger مع مهلة 0

مرة أخرى ، وفقًا لـ "UNIX Network Programming" ، الطبعة الثالثة صفحة 202-203 ، الإعداد SO_LINGER مع مهلة 0 قبل الاتصال close() سوف يتسبب في تسلسل الإنهاء الطبيعي ليس لبدء.

بدلاً من ذلك ، يقوم الأقران بإعداد هذا الخيار والاتصال close() سوف يرسل RST (إعادة تعيين الاتصال) الذي يشير إلى حالة خطأ وهذا هو كيف سيتم إدراكه في الطرف الآخر. سترى عادة أخطاء مثل "إعادة ضبط الاتصال بواسطة Peer".

لذلك ، في الوضع الطبيعي ، إنها فكرة سيئة حقًا SO_LINGER مع مهلة 0 قبل الاتصال close() - من الآن فصاعدًا إضداد إغلاق - في تطبيق الخادم.

ومع ذلك ، فإن بعض الموقف يستدعي القيام بذلك على أي حال:

  • إذا أسيء تصرف عميل تطبيق الخادم الخاص بك (مرات خارج ، فإن إرجاع البيانات غير الصالحة ، وما إلى ذلك) ، فإن الإغلاق الإضافي أمر منطقي لتجنب التعثر CLOSE_WAIT أو ينتهي في TIME_WAIT حالة.
  • إذا كان عليك إعادة تشغيل تطبيق الخادم الخاص بك والذي يحتوي حاليًا على الآلاف من اتصالات العميل ، فقد تفكر في تعيين خيار المقبس هذا لتجنب آلاف مآخذ الخادم في TIME_WAIT (عند الاتصال close() من نهاية الخادم) لأن هذا قد يمنع الخادم من الحصول على منافذ متوفرة لاتصالات العميل الجديدة بعد إعادة تشغيلها.
  • في الصفحة 202 في الكتاب المذكور أعلاه ، يقول على وجه التحديد: "هناك ظروف معينة تستدعي استخدام هذه الميزة لإرسال إغلاق فاشل. أحد الأمثلة على ذلك خادم طرفي RS-232 ، والذي قد يتم تعليقه إلى الأبد في CLOSE_WAIT محاولة توصيل البيانات إلى منفذ طرفي عالق ، ولكن سيتم إعادة ضبط المنفذ المعلق بشكل صحيح إذا حصل على RST لتجاهل البيانات المعلقة. "

أوصي هذه مقال طويل أعتقد أنه يعطي إجابة جيدة جدًا على سؤالك.

عندما يكون الباقين في وضع التشغيل ولكن المهلة هي صفر ، لا تنتظر مكدس TCP لإرسال البيانات المعلقة قبل إغلاق الاتصال. يمكن أن تضيع البيانات بسبب هذا ، ولكن من خلال تعيين باقية بهذه الطريقة ، فأنت تقبل ذلك وتطلب إعادة تعيين الاتصال على الفور بدلاً من الإغلاق بأمان. هذا يتسبب في إرسال الأعمال بدلاً من الزعنفة المعتادة.

بفضل EJP على تعليقه ، انظر هنا للتفاصيل.

سواءً كان بإمكانك إزالة الباقية في الكود الخاص بك بأمان أو لا يعتمد على نوع التطبيق الخاص بك: هل هو "عميل" (فتح اتصالات TCP وإغلاقه بنشاط أولاً) أو هل هو "خادم" (الاستماع إلى TCP مفتوح و إغلاقه بعد أن بدأ الجانب الآخر الإغلاق)؟

إذا كان للتطبيق الخاص بك نكهة "عميل" (إغلاق أولاً) وقمت بتشغيل عدد كبير من الاتصالات وإغلاقها إلى خوادم مختلفة (على سبيل المثال عندما يكون تطبيقك هو تطبيق مراقبة يشرف على إمكانية الوصول إلى عدد كبير من الخوادم المختلفة) تطبيقك لديه مشكلة أن جميع اتصالات العميل الخاصة بك عالقة في حالة time_wait. بعد ذلك ، أوصي بتقصير المهلة إلى قيمة أصغر من الافتراضي الذي لا يزال الإغلاق بأمان ولكن تحرير موارد اتصالات العميل في وقت مبكر. لن أقوم بتعيين المهلة على 0 ، لأن 0 لا يتم إيقاف تشغيله بأمان مع FIN ولكنه فاشل مع RST.

إذا كان للتطبيق الخاص بك نكهة "عميل" ويتعين عليه جلب كمية كبيرة من الملفات الصغيرة من نفس الخادم ، فيجب ألا تبدأ اتصال TCP جديد لكل ملف وينتهي بك الأمر بكمية ضخمة من اتصالات العميل في Time_wait ، ولكن الحفاظ على الاتصال مفتوحا وجلب جميع البيانات على نفس الاتصال. خيار بانجر يمكن وينبغي إزالته.

إذا كان التطبيق الخاص بك هو "خادم" (أغلق في الثانية كرد فعل على إغلاق Peer) ، في Close () يتم إيقاف الاتصال الخاص بك بأمان ويتم تحرير الموارد حيث لا تدخل Time_wait State. لا ينبغي استخدام الباقي. ولكن إذا كان تطبيق SEVER الخاص بك يحتوي على عملية إشرافية تكتشف الاتصالات المفتوحة غير النشطة الخمول لفترة طويلة ("طويل") ، فيمكنك إغلاق هذا الاتصال غير النشط من جانبك - انظر إلى نوع من معالجة الأخطاء - مع إيقاف تشغيل فاشل. يتم ذلك عن طريق تعيين مهلة متطورة إلى 0. SLIVE () ثم سيتم إرسال عرض إلى العميل ، وأخبره أنك غاضب :-)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top