SO_REUSEADDR और SO_REUSEPORT कैसे भिन्न होते हैं?


663

man pagesऔर सॉकेट विकल्प के लिए प्रोग्रामर दस्तावेजों SO_REUSEADDRऔर SO_REUSEPORTअलग ऑपरेटिंग सिस्टम के लिए अलग और अक्सर अत्यधिक भ्रमित कर रहे हैं। कुछ ऑपरेटिंग सिस्टम का विकल्प भी नहीं है SO_REUSEPORT। WEB इस विषय के बारे में जानकारी के विरोधाभासी से भरा है और अक्सर आप ऐसी जानकारी पा सकते हैं जो केवल एक विशिष्ट ऑपरेटिंग सिस्टम के एक सॉकेट कार्यान्वयन के लिए सही है, जिसे पाठ में स्पष्ट रूप से उल्लेखित नहीं किया जा सकता है।

तो वास्तव में कैसे SO_REUSEADDRअलग है SO_REUSEPORT?

क्या सिस्टम बिना SO_REUSEPORTअधिक सीमित हैं?

और अगर मैं अलग-अलग ऑपरेटिंग सिस्टम पर एक का उपयोग करता हूं तो वास्तव में अपेक्षित व्यवहार क्या है?

जवाबों:


1615

पोर्टेबिलिटी की अद्भुत दुनिया में आपका स्वागत है ... या इसकी कमी है। इससे पहले कि हम इन दो विकल्पों का विस्तार से विश्लेषण करना शुरू करें और गहराई से देखें कि विभिन्न ऑपरेटिंग सिस्टम उन्हें कैसे संभालते हैं, यह ध्यान दिया जाना चाहिए कि बीएसडी सॉकेट कार्यान्वयन सभी सॉकेट कार्यान्वयन की मां है। मूल रूप से अन्य सभी प्रणालियों ने कुछ समय में (या कम से कम इसके इंटरफेस) में बीएसडी सॉकेट कार्यान्वयन की नकल की और फिर इसे अपने दम पर विकसित करना शुरू कर दिया। बेशक बीएसडी सॉकेट कार्यान्वयन एक ही समय में विकसित किया गया था और इस प्रकार इसे कॉपी करने वाली प्रणालियों को बाद में ऐसी विशेषताएं मिलीं जो उन प्रणालियों में कमी थीं जिन्हें पहले कॉपी किया गया था। बीएसडी सॉकेट कार्यान्वयन को समझना अन्य सॉकेट कार्यान्वयन को समझने की कुंजी है, इसलिए आपको इसके बारे में पढ़ना चाहिए, भले ही आपको बीएसडी सिस्टम के लिए कोड लिखने की परवाह न हो।

इन दो विकल्पों को देखने से पहले आपको कुछ मूलभूत बातें बताई जानी चाहिए। एक टीसीपी / यूडीपी कनेक्शन की पहचान पांच मूल्यों के एक टपल द्वारा की जाती है:

{<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}

इन मूल्यों का कोई भी अनूठा संयोजन एक कनेक्शन की पहचान करता है। परिणामस्वरूप, किसी भी दो कनेक्शन में समान पाँच मान नहीं हो सकते हैं, अन्यथा सिस्टम इन कनेक्शनों को किसी भी तरह से अलग नहीं कर पाएगा।

सॉकेट का प्रोटोकॉल तब सेट किया जाता है जब socket()फ़ंक्शन के साथ सॉकेट बनाया जाता है । स्रोत का पता और पोर्ट bind()फ़ंक्शन के साथ सेट किया गया है। गंतव्य पता और पोर्ट connect()फ़ंक्शन के साथ सेट किए गए हैं। चूंकि यूडीपी एक कनेक्शन रहित प्रोटोकॉल है, यूडीपी सॉकेट्स का उपयोग उन्हें कनेक्ट किए बिना किया जा सकता है। फिर भी इसे उन्हें और कुछ मामलों में आपके कोड और सामान्य एप्लिकेशन डिज़ाइन के लिए बहुत फायदेमंद है। कनेक्शन रहित मोड में, यूडीपी सॉकेट्स जो स्पष्ट रूप से बाध्य नहीं थे जब पहली बार डेटा उनके ऊपर भेजा जाता है, आमतौर पर सिस्टम द्वारा स्वचालित रूप से बाध्य होता है, क्योंकि एक अनबाउंड यूडीपी सॉकेट किसी भी (उत्तर) डेटा प्राप्त नहीं कर सकता है। एक अनबाउंड टीसीपी सॉकेट के लिए समान है, यह कनेक्ट होने से पहले स्वचालित रूप से बाध्य है।

यदि आप स्पष्ट रूप से एक सॉकेट बांधते हैं, तो इसे पोर्ट से बांधना संभव है 0, जिसका अर्थ है "कोई भी पोर्ट"। चूँकि सॉकेट वास्तव में सभी मौजूदा बंदरगाहों के लिए बाध्य नहीं हो सकता है, सिस्टम को उस मामले में स्वयं एक विशिष्ट पोर्ट चुनना होगा (आमतौर पर पूर्वनिर्धारित, स्रोत पोर्ट के ओएस विशिष्ट रेंज से)। स्रोत पते के लिए एक समान वाइल्डकार्ड मौजूद है, जो "किसी भी पते" ( 0.0.0.0आईपीवी 4 के मामले में) और हो सकता है::IPv6 के मामले में)। बंदरगाहों के मामले में, एक सॉकेट वास्तव में "किसी भी पते" के लिए बाध्य हो सकता है, जिसका अर्थ है "सभी स्थानीय इंटरफेस के सभी स्रोत आईपी पते"। यदि सॉकेट बाद में जुड़ा हुआ है, तो सिस्टम को एक विशिष्ट स्रोत आईपी पता चुनना होगा, क्योंकि एक सॉकेट कनेक्ट नहीं किया जा सकता है और उसी समय किसी भी स्थानीय आईपी पते के लिए बाध्य होना चाहिए। गंतव्य पते और राउटिंग टेबल की सामग्री के आधार पर, सिस्टम एक उपयुक्त स्रोत पते को ले जाएगा और चुने हुए स्रोत आईपी पते के लिए बाध्यकारी के साथ "किसी भी" बंधन को बदल देगा।

डिफ़ॉल्ट रूप से, कोई भी दो सॉकेट स्रोत पते और स्रोत पोर्ट के समान संयोजन के लिए बाध्य नहीं हो सकते हैं। जब तक स्रोत पोर्ट अलग होता है, तब तक स्रोत का पता वास्तव में अप्रासंगिक होता है। बाइंडिंग socketAके लिए A:Xऔर socketBकरने के लिए B:Yहै, जहां Aऔर Bपते दिए गए हैं और Xऔर Yबंदरगाह हैं, हमेशा की तरह लंबे समय के रूप में संभव है X != Yसच है। हालांकि, भले ही X == Y, बाध्यकारी अभी भी संभव है जब तक कि A != Bसच है। उदाहरण के लिए socketAएक FTP सर्वर कार्यक्रम के अंतर्गत आता है और के लिए बाध्य है 192.168.0.1:21और socketBएक और FTP सर्वर कार्यक्रम के अंतर्गत आता है और के लिए बाध्य है 10.0.0.1:21, दोनों बाइंडिंग सफल होगा। हालांकि, ध्यान रखें कि एक सॉकेट "किसी भी पते" के लिए स्थानीय रूप से बाध्य हो सकता है। यदि एक सॉकेट के लिए बाध्य है0.0.0.0:21, यह एक ही समय में सभी मौजूदा स्थानीय पतों के लिए बाध्य है और उस स्थिति में कोई भी अन्य सॉकेट पोर्ट के लिए बाध्य नहीं किया जा सकता है 21, चाहे वह किसी भी विशिष्ट आईपी पते से हो, 0.0.0.0जो सभी मौजूदा स्थानीय आईपी पतों के साथ टकराव के रूप में बाँधने की कोशिश करता है ।

कुछ भी कहा अब तक सभी प्रमुख ऑपरेटिंग सिस्टम के लिए बहुत समान है। जब पता पुन: उपयोग में आता है तो चीजें ओएस विशिष्ट होने लगती हैं। हम बीएसडी के साथ शुरू करते हैं, जैसा कि मैंने ऊपर कहा था, यह सभी सॉकेट कार्यान्वयन की मां है।

बीएसडी

SO_REUSEADDR

यदि SO_REUSEADDRयह बाध्यकारी करने से पहले एक सॉकेट पर सक्षम है, सॉकेट सफलतापूर्वक बाध्य किया जा सकता है जब तक कि वहाँ के लिए बाध्य एक और सॉकेट के साथ एक संघर्ष है वास्तव में स्रोत पता और पोर्ट के समान संयोजन। अब आप सोच सकते हैं कि यह पहले से अलग कैसे है? कीवर्ड "बिल्कुल" है। SO_REUSEADDRमुख्य रूप से वाइल्डकार्ड पते ("किसी भी आईपी पते") का तरीका बदल जाता है जब संघर्षों की खोज की जाती है।

बिना SO_REUSEADDR, बंधन socketAको 0.0.0.0:21और फिर बंधन socketBको 192.168.0.1:21(त्रुटि के साथ विफल हो जाएगा EADDRINUSE,) के बाद से 0.0.0.0 का अर्थ है "किसी भी स्थानीय आईपी पते", इस प्रकार सभी स्थानीय IP पते इस सॉकेट द्वारा उपयोग में माना जाता है और यह भी शामिल है 192.168.0.1भी,। साथ SO_REUSEADDRयह बाद से, सफल होगा 0.0.0.0और 192.168.0.1कर रहे हैं वास्तव में नहीं एक ही पते, एक सभी स्थानीय पते का वाइल्डकार्ड है और अन्य एक एक बहुत विशिष्ट स्थानीय पता है। ध्यान दें कि उपर्युक्त कथन सत्य है, भले ही वह किस क्रम में हो socketAऔर socketBबाध्य हो; इसके बिना SO_REUSEADDRयह हमेशा विफल रहेगा, इसके साथ SO_REUSEADDRयह हमेशा सफल होगा।

आपको एक बेहतर अवलोकन देने के लिए, आइए यहां एक तालिका बनाएं और सभी संभावित संयोजनों को सूचीबद्ध करें:

SO_REUSEADDR सॉकेट ए सॉकेटबी परिणाम
-------------------------------------------------- -------------------
  ON / OFF 192.168.0.1:21 192.168.0.1:21 त्रुटि (EADDRINUSE)
  ON / OFF 192.168.0.1:21 10.0.0.1:21 ठीक है
  पर / बंद 10.0.0.1:21 192.168.0.1:21 ठीक है
   OFF 0.0.0.0:21 192.168.1.0:21 त्रुटि (EADDRINUSE)
   बंद 192.168.1.0:21 0.0.0.0:21 त्रुटि (EADDRINUSE)
   0.0.0.0:21 192.168.1.0:21 ठीक है
   192.168.1.0:21 0.0.0.0:21 ठीक है
  ON / OFF 0.0.0.0:21 0.0.0.0:21 त्रुटि (EADDRINUSE)

ऊपर दी गई तालिका मानती है कि socketAपहले से दिए गए पते के लिए सफलतापूर्वक बाध्य किया गया है socketA, तो socketBबनाया जाता है, या तो SO_REUSEADDRसेट हो जाता है या नहीं, और अंत में दिए गए पते के लिए बाध्य होता है socketBResultके लिए बाइंड ऑपरेशन का परिणाम है socketB। यदि पहला कॉलम कहता है ON/OFF, SO_REUSEADDRतो परिणाम का मान अप्रासंगिक है।

ठीक है, SO_REUSEADDRवाइल्डकार्ड पते पर प्रभाव पड़ता है, यह जानना अच्छा है। अभी तक यह नहीं है कि यह केवल प्रभाव है। एक और अच्छी तरह से ज्ञात प्रभाव है, यही कारण है कि ज्यादातर लोग SO_REUSEADDRपहली बार सर्वर प्रोग्राम में उपयोग करते हैं । इस विकल्प के अन्य महत्वपूर्ण उपयोग के लिए हमें इस बात पर गहन विचार करना होगा कि टीसीपी प्रोटोकॉल कैसे काम करता है।

एक सॉकेट में एक भेज बफर होता है और यदि send()फ़ंक्शन में कॉल सफल होता है, तो इसका मतलब यह नहीं है कि अनुरोधित डेटा वास्तव में वास्तव में भेजा गया है, इसका मतलब केवल डेटा को बफर में जोड़ा गया है। यूडीपी सॉकेट्स के लिए, डेटा आमतौर पर बहुत जल्द भेजा जाता है, यदि तुरंत नहीं, लेकिन टीसीपी सॉकेट्स के लिए, डेटा को भेजने वाले बफर में जोड़ने और टीसीपी कार्यान्वयन वास्तव में उस डेटा को भेजने के बीच अपेक्षाकृत लंबे समय तक देरी हो सकती है। परिणामस्वरूप, जब आप एक टीसीपी सॉकेट बंद करते हैं, तो अभी भी भेजने वाले बफर में डेटा लंबित हो सकता है, जिसे अभी तक नहीं भेजा गया है, लेकिन आपका कोड इसे भेजे जाने के रूप में मानता है, क्योंकिsend()कॉल सफल रहा। यदि टीसीपी कार्यान्वयन आपके अनुरोध पर तुरंत सॉकेट बंद कर रहा था, तो यह सभी डेटा खो जाएगा और आपके कोड को इसके बारे में पता भी नहीं होगा। टीसीपी को एक विश्वसनीय प्रोटोकॉल कहा जाता है और डेटा को वैसे ही खोना बहुत विश्वसनीय नहीं है। यही कारण है कि एक सॉकेट जिसमें अभी भी भेजने के लिए डेटा है वह एक राज्य में जाएगा जिसे TIME_WAITआप इसे बंद करते हैं। उस स्थिति में यह तब तक प्रतीक्षा करेगा जब तक कि सभी लंबित डेटा को सफलतापूर्वक नहीं भेजा गया हो या जब तक कोई समय समाप्त न हो जाए, उस स्थिति में सॉकेट को बलपूर्वक बंद कर दिया जाता है।

सॉकेट बंद होने से पहले कर्नेल की प्रतीक्षा करने की अवधि, चाहे वह अभी भी फ्लाइट में डेटा हो या नहीं, को Linger Time कहा जाता है । अभी भी ताजा समय (दो मिनट एक आम मूल्य आप कई सिस्टम पर मिलेगा है) सबसे सिस्टम पर और डिफ़ॉल्ट बल्कि लंबे द्वारा विश्व स्तर पर विन्यास योग्य है। यह सॉकेट विकल्प का उपयोग करके प्रति सॉकेट के लिए भी कॉन्फ़िगर करने योग्य है SO_LINGERजिसका उपयोग टाइमआउट को कम या लंबे समय तक करने के लिए किया जा सकता है, और यहां तक ​​कि पूरी तरह से अक्षम करने के लिए भी। इसे पूरी तरह से अक्षम करना एक बहुत ही बुरा विचार है, हालाँकि, चूंकि टीसीपी सॉकेट को इनायत से बंद करना एक थोड़ी जटिल प्रक्रिया है और इसमें कुछ पैकेटों को भेजने और वापस भेजने के साथ-साथ उन पैकेटों को फिर से बनाना भी शामिल है, जो खो गए हैं) और यह पूरी तरह से प्रक्रिया है Linger Time द्वारा भी सीमित है। यदि आप लिंग को अक्षम कर देते हैं, तो आपका सॉकेट न केवल उड़ान में डेटा खो सकता है, यह हमेशा शालीनता के बजाय जबरदस्ती बंद कर दिया जाता है, जिसे आमतौर पर अनुशंसित नहीं किया जाता है। टीसीपी कनेक्शन को कैसे इनायत से बंद किया जाता है, इसके बारे में विवरण इस उत्तर के दायरे से परे है, यदि आप इसके बारे में अधिक जानना चाहते हैं, तो मैं आपको इस पृष्ठ पर एक नज़र डालने की सलाह देता हूं । और भले ही आप के साथ लिंचिंग को अक्षम कर दिया हो SO_LINGER, अगर आपकी प्रक्रिया सॉकेट को बंद किए बिना मर जाती है, तो बीएसडी (और संभवत: अन्य सिस्टम) आपके द्वारा कॉन्फ़िगर किए गए की अनदेखी करते हुए, फिर भी अदरक करेगा। यह उदाहरण के लिए होगा यदि आपका कोड सिर्फ कॉल करता हैexit()(छोटे, सरल सर्वर कार्यक्रमों के लिए बहुत ही सामान्य) या प्रक्रिया एक सिग्नल द्वारा मार दी जाती है (जिसमें यह संभावना भी शामिल है कि यह अवैध मेमोरी एक्सेस के कारण क्रैश हो जाए)। इसलिए ऐसा कुछ भी नहीं है जो आप यह सुनिश्चित करने के लिए कर सकते हैं कि एक सॉकेट सभी परिस्थितियों में कभी भी सुस्त नहीं होगा।

सवाल यह है कि सिस्टम राज्य में सॉकेट का इलाज कैसे करता है TIME_WAIT? यदि SO_REUSEADDRसेट नहीं किया गया है, तो राज्य में एक सॉकेट TIME_WAITको अभी भी स्रोत पते और पोर्ट के लिए बाध्य माना जाता है और किसी नए सॉकेट को उसी पते और पोर्ट पर बाँधने का कोई भी प्रयास तब तक विफल रहेगा जब तक कि सॉकेट वास्तव में बंद नहीं हो जाता है, जिसमें लंबा समय लग सकता है कॉन्फ़िगर किए गए Linger Time के रूप में । इसलिए उम्मीद न करें कि आप इसे बंद करने के तुरंत बाद एक सॉकेट के स्रोत पते को फिर से खोल सकते हैं। ज्यादातर मामलों में यह विफल हो जाएगा। हालाँकि, यदि SO_REUSEADDRआप उस सॉकेट के लिए सेट हैं जिसे आप बांधने का प्रयास कर रहे हैं, तो राज्य में उसी पते और पोर्ट से जुड़ा एक और सॉकेटTIME_WAITबस इसकी पहले से ही "आधी मौत" के बाद उपेक्षा की जाती है, और आपका सॉकेट बिना किसी समस्या के ठीक उसी पते पर बंध सकता है। उस मामले में यह कोई भूमिका नहीं निभाता है कि अन्य सॉकेट में बिल्कुल समान पता और पोर्ट हो सकता है। ध्यान दें कि एक सॉकेट को उसी पते और पोर्ट को TIME_WAITराज्य में मरने वाले सॉकेट के रूप में बांधना अप्रत्याशित हो सकता है, और आमतौर पर अवांछित, साइड इफेक्ट के मामले में अन्य सॉकेट अभी भी "काम पर" है, लेकिन यह इस उत्तर के दायरे से परे है और सौभाग्य से वे दुष्प्रभाव व्यवहार में दुर्लभ हैं।

एक अंतिम बात है जिसके बारे में आपको पता होना चाहिए SO_REUSEADDR। ऊपर लिखा हुआ सब कुछ तब तक काम करेगा जब तक आप जिस सॉकेट को बाइंड करना चाहते हैं वह एड्रेस रीएक्टिव इनेबल हो जाए। यह आवश्यक नहीं है कि अन्य सॉकेट, वह जो पहले से ही बंधा हुआ है या एक TIME_WAITस्थिति में है, यह ध्वज भी निर्धारित किया गया था जब यह बाध्य था। वह कोड जो यह तय करता है कि बाँध सफल होगा या विफल , कॉल SO_REUSEADDRमें खिलाए गए सॉकेट के ध्वज का निरीक्षण करता है bind(), अन्य सभी सॉकेटों का निरीक्षण करने के लिए, इस ध्वज को देखा भी नहीं जाता है।

SO_REUSEPORT

SO_REUSEPORTज्यादातर लोग क्या SO_REUSEADDRहोने की उम्मीद करेंगे । मूल रूप से, SO_REUSEPORTआप सॉकेट की एक मनमानी संख्या को ठीक उसी स्रोत पते और पोर्ट से बाँधने की अनुमति देते हैं , जब तक कि सभी पूर्व बाध्य सॉकेट्स भी SO_REUSEPORTनिर्धारित नहीं हो जाते थे। यदि पहला सॉकेट जो किसी पते से जुड़ा होता है और पोर्ट SO_REUSEPORTसेट नहीं होता है, तो कोई अन्य सॉकेट ठीक उसी पते और पोर्ट से बाध्य नहीं हो सकता है, भले ही यह अन्य सॉकेट SO_REUSEPORTसेट किया गया हो या नहीं, जब तक कि पहला सॉकेट फिर से अपने बंधन को जारी नहीं करता। SO_REUESADDRकोड हैंडलिंग के विपरीत, SO_REUSEPORTन केवल यह सत्यापित करेगा कि वर्तमान में बाध्य सॉकेट ने SO_REUSEPORTसेट किया है, बल्कि यह भी सत्यापित करेगा कि एक परस्पर विरोधी पते और पोर्ट के साथ सॉकेट SO_REUSEPORTसेट किया गया था जब यह बाध्य था।

SO_REUSEPORTमतलब नहीं है SO_REUSEADDR। इसका मतलब यह है कि यदि सॉकेट SO_REUSEPORTसेट नहीं किया गया था जब वह बाध्य था और एक अन्य सॉकेट SO_REUSEPORTसेट हो गया है जब वह बिल्कुल उसी पते और पोर्ट से बंधा हुआ है, तो बाइंड विफल हो जाता है, जो अपेक्षित है, लेकिन यह भी विफल हो जाता है यदि अन्य सॉकेट पहले से ही मर रहा है और TIME_WAITराज्य में है। एक सॉकेट को एक ही पते पर बांधने में सक्षम होने के लिए और TIME_WAITराज्य में एक और सॉकेट को पोर्ट SO_REUSEADDRकरने के लिए या तो उस सॉकेट पर सेट करने की आवश्यकता होती है या उन्हें बाइंड करने से पहले दोनों सॉकेट परSO_REUSEPORT सेट किया जाना चाहिए । बेशक यह दोनों को सेट करने की अनुमति है, SO_REUSEPORTऔर SO_REUSEADDR, एक सॉकेट पर।

इसके SO_REUSEPORTअलावा इसके बारे में कहने के लिए बहुत कुछ नहीं है SO_REUSEADDR, इसलिए इसे बाद में अन्य प्रणालियों के कई सॉकेट कार्यान्वयनों में नहीं मिलेगा, जो इस विकल्प को जोड़ने से पहले बीएसडी कोड को "forked" किया था, और यह कि कोई भी नहीं था इस विकल्प से पहले बीएसडी में एक ही सॉकेट पते के लिए दो सॉकेट को बांधने का तरीका।

कनेक्ट () रिटर्निंग EADDRINUSE?

ज्यादातर लोग जानते हैं कि bind()त्रुटि के साथ विफल हो सकता है EADDRINUSE, हालांकि, जब आप पता पुन: उपयोग के साथ खेलना शुरू करते हैं, तो आप उस अजीब स्थिति में भाग सकते हैं जो connect()उस त्रुटि के साथ भी विफल हो जाती है। यह कैसे हो सकता है? एक दूरस्थ पता कैसे हो सकता है, आखिर कनेक्ट जो एक सॉकेट में जोड़ता है, पहले से ही उपयोग में है? एक ही रिमोट एड्रेस से कई सॉकेट कनेक्ट करने से पहले कभी कोई समस्या नहीं हुई, तो यहां क्या गलत हो रहा है?

जैसा कि मैंने अपने उत्तर के शीर्ष पर कहा था, एक कनेक्शन को पांच मूल्यों के एक टपल द्वारा परिभाषित किया गया है, याद है? और मैंने यह भी कहा, कि ये पांच मूल्य अद्वितीय होने चाहिए अन्यथा सिस्टम दो कनेक्शनों को अलग नहीं कर सकता है, है ना? ठीक है, पते के पुन: उपयोग के साथ, आप एक ही प्रोटोकॉल के दो सॉकेट को एक ही स्रोत पते और पोर्ट पर बांध सकते हैं। इसका मतलब है कि उन पांच मूल्यों में से तीन पहले से ही इन दो सॉकेट्स के लिए समान हैं। यदि आप अब इन दोनों सॉकेट्स को एक ही गंतव्य पते और पोर्ट से कनेक्ट करने का प्रयास करते हैं, तो आप दो कनेक्ट किए गए सॉकेट्स बनाएंगे, जिनके ट्यूप बिल्कुल समान हैं। यह काम नहीं कर सकता, कम से कम टीसीपी कनेक्शन के लिए नहीं (यूडीपी कनेक्शन वैसे भी वास्तविक कनेक्शन नहीं हैं)। यदि डेटा दोनों में से किसी एक कनेक्शन के लिए आता है, तो सिस्टम यह नहीं बता सकता कि डेटा किस कनेक्शन से संबंधित है।

इसलिए यदि आप एक ही प्रोटोकॉल के दो सॉकेट्स को एक ही स्रोत पते और पोर्ट पर बाँधते हैं और उन दोनों को एक ही गंतव्य पते और पोर्ट से जोड़ने का प्रयास करते हैं, connect()तो वास्तव में EADDRINUSEआप जिस दूसरे सॉकेट को कनेक्ट करने का प्रयास करते हैं, उसके लिए त्रुटि के साथ विफल हो जाएगा , जिसका अर्थ है कि ए पांच मानों के एक समान टपल के साथ सॉकेट पहले से जुड़ा हुआ है।

मल्टिकास्ट पते

अधिकांश लोग इस तथ्य की अनदेखी करते हैं कि मल्टीकास्ट पते मौजूद हैं, लेकिन वे मौजूद हैं। जबकि यूनिकस्ट एड्रेस एक-से-एक संचार के लिए उपयोग किए जाते हैं, मल्टीकास्ट पते एक से कई संचार के लिए उपयोग किए जाते हैं। अधिकांश लोगों को आईपीवी 6 के बारे में पता चलने पर मल्टीकास्ट पते के बारे में पता चला, लेकिन आईपीवी 4 में मल्टीकास्ट पते भी मौजूद थे, भले ही यह सुविधा सार्वजनिक इंटरनेट पर व्यापक रूप से उपयोग नहीं की गई थी।

SO_REUSEADDRमल्टीकास्ट पतों के लिए परिवर्तनों का अर्थ है क्योंकि यह कई सॉकेट को स्रोत मल्टीकास्ट एड्रेस और पोर्ट के बिल्कुल समान संयोजन के लिए बाध्य करने की अनुमति देता है। दूसरे शब्दों में, बहुस्त्र्पीय पतों के लिए SO_REUSEADDRवैसा ही व्यवहार करता है जैसा SO_REUSEPORTकि यूनिकस्ट पतों के लिए। वास्तव में, कोड व्यवहार करता है SO_REUSEADDRऔर SO_REUSEPORTमल्टीकास्ट पतों के लिए समान रूप से, कि साधन आपको लगता है कि कह सकते हैं SO_REUSEADDRका तात्पर्य SO_REUSEPORTसभी मल्टीकास्ट पतों और दूसरी तरह के दौर के लिए।


FreeBSD / OpenBSD / NetBSD

ये सभी मूल बीएसडी कोड के बजाय देर से कांटे हैं, यही कारण है कि वे तीनों बीएसडी के समान विकल्प प्रदान करते हैं और वे बीएसडी की तरह ही व्यवहार करते हैं।


MacOS (MacOS X)

इसके मूल में, बीएसडी कोड (बीएसडी 4.3) के अधिक देर के कांटे के आधार पर , मैकओएस बस एक बीएसडी-शैली यूनिक्स है, जिसे " डार्विन " कहा जाता है, जो बाद में उस समय के साथ फिर से सिंक्रनाइज़ हो गया था (उस समय वर्तमान: फ्रीबीएसडी) मैक ओएस 10.3 रिलीज़ के लिए 5 कोड बेस, ताकि ऐप्पल पूरी पॉसिक्स अनुपालन हासिल कर सके (मैकओएस पोज़िक्स प्रमाणित है)। इसके मूल (" मच ") में एक माइक्रोकर्नेल होने के बावजूद , शेष कर्नेल (" XNU ") मूल रूप से बस बीएसडी कर्नेल है, और इसीलिए macOS BSD के समान ही विकल्प प्रदान करता है और वे BSD की तरह ही व्यवहार करते हैं। ।

iOS / watchOS / tvOS

iOS थोड़े संशोधित और छंटनी किए गए कर्नेल के साथ सिर्फ एक macOS कांटा है, जो कुछ हद तक उपयोगकर्ता स्पेस टूलसेट और थोड़ा अलग डिफ़ॉल्ट सेट सेट के साथ है। वॉचओएस और टीवीओएस आईओएस फोर्क्स हैं, जो आगे भी (विशेष रूप से वॉचओएस) नीचे छीन लिए जाते हैं। मेरे सबसे अच्छे ज्ञान के लिए वे सभी बिल्कुल वैसा ही व्यवहार करते हैं जैसा कि macOS करता है।


लिनक्स

लिनक्स <3.9

लिनक्स 3.9 से पहले, केवल विकल्प SO_REUSEADDRमौजूद था। यह विकल्प आमतौर पर बीएसडी में दो महत्वपूर्ण अपवादों के साथ व्यवहार करता है:

  1. जब तक एक श्रवण (सर्वर) टीसीपी सॉकेट एक विशिष्ट पोर्ट से जुड़ा SO_REUSEADDRहोता है , तब तक उस पोर्ट को लक्षित करने वाले सभी सॉकेट्स के लिए विकल्प को पूरी तरह से अनदेखा कर दिया जाता है। एक ही पोर्ट पर दूसरा सॉकेट बांधना केवल तभी संभव है जब यह बीएसडी में SO_REUSEADDRसेट किए बिना भी संभव था । उदाहरण के लिए, आप वाइल्डकार्ड पते पर नहीं जा सकते हैं और फिर एक अधिक विशिष्ट एक या दूसरे तरीके से राउंड, दोनों बीएसडी में संभव है यदि आप सेट करते हैं SO_REUSEADDR। आप जो कर सकते हैं वह है कि आप एक ही पोर्ट और दो अलग-अलग नॉन-वाइल्डकार्ड एड्रेस को बांध सकते हैं, जैसा कि हमेशा अनुमति है। इस पहलू में लिनक्स बीएसडी की तुलना में अधिक प्रतिबंधात्मक है।

  2. दूसरा अपवाद यह है कि क्लाइंट सॉकेट्स के लिए, यह विकल्प बिल्कुल SO_REUSEPORTबीएसडी की तरह व्यवहार करता है , जब तक कि दोनों को यह ध्वज सेट किया गया था, इससे पहले कि वे बाध्य थे। यह अनुमति देने का कारण यह था कि विभिन्न प्रोटोकॉल के लिए एक ही यूडीपी सॉकेट पते के लिए कई सॉकेट को बाँधने में सक्षम होना महत्वपूर्ण है और जैसा कि SO_REUSEPORT3.9 से पहले नहीं हुआ करता SO_REUSEADDRथा , उस अंतराल को भरने के लिए तदनुसार व्यवहार को बदल दिया गया था। । उस पहलू में लिनक्स बीएसडी से कम प्रतिबंधात्मक है।

लिनक्स> = 3.9

Linux 3.9 ने SO_REUSEPORTLinux में भी विकल्प जोड़ा । यह विकल्प बिल्कुल बीएसडी में विकल्प की तरह व्यवहार करता है और जब तक सभी सॉकेट्स को बाइंड करने से पहले सेट नहीं किया जाता है, तब तक यह पते और पोर्ट नंबर के समान ही बाइंडिंग की अनुमति देता है।

फिर भी, SO_REUSEPORTअन्य प्रणालियों पर अभी भी दो अंतर हैं:

  1. "पोर्ट हाइजैकिंग" को रोकने के लिए, एक विशेष सीमा है: सभी सॉकेट जो एक ही पते को साझा करना चाहते हैं और पोर्ट संयोजन को उसी प्रभावी उपयोगकर्ता आईडी को साझा करने वाली प्रक्रियाओं से संबंधित होना चाहिए! इसलिए एक उपयोगकर्ता दूसरे उपयोगकर्ता के पोर्ट को "चोरी" नहीं कर सकता है। लापता SO_EXCLBIND/ SO_EXCLUSIVEADDRUSEझंडे के लिए कुछ हद तक क्षतिपूर्ति करने के लिए यह कुछ विशेष जादू है ।

  2. इसके अतिरिक्त कर्नेल कुछ "विशेष जादू" SO_REUSEPORTकरता है जो अन्य ऑपरेटिंग सिस्टमों में नहीं पाया जाता है: यूडीपी सॉकेट्स के लिए, यह डेटाग्राम को समान रूप से वितरित करने की कोशिश करता है, टीसीपी सुनने वाले सॉकेट्स के लिए, यह आने वाले कनेक्ट रिक्वेस्ट वितरित करने का प्रयास करता है (जिन्हें कॉलिंग द्वारा स्वीकार किया जाता है accept()) समान रूप से सभी सॉकेट्स में जो समान पता और पोर्ट संयोजन साझा करते हैं। इस प्रकार एक एप्लिकेशन कई बाल प्रक्रियाओं में समान पोर्ट आसानी से खोल सकता है और फिर SO_REUSEPORTएक बहुत सस्ती लोड संतुलन प्राप्त करने के लिए उपयोग कर सकता है।


एंड्रॉयड

भले ही संपूर्ण एंड्रॉइड सिस्टम अधिकांश लिनक्स वितरणों से कुछ अलग है, इसके मूल में थोड़ा संशोधित लिनक्स कर्नेल काम करता है, इस प्रकार लिनक्स पर लागू होने वाली हर चीज एंड्रॉइड पर भी लागू होनी चाहिए।


खिड़कियाँ

विंडोज केवल SO_REUSEADDRविकल्प जानता है , कोई भी नहीं है SO_REUSEPORTSO_REUSEADDRविंडोज में सॉकेट पर सेट करना बीएसडी में एक सॉकेट पर SO_REUSEPORTऔर SO_REUSEADDRएक अपवाद के साथ, एक अपवाद के रूप में व्यवहार करता है: एक सॉकेट SO_REUSEADDRहमेशा एक ही स्रोत के पते और पोर्ट को पहले से बंधे सॉकेट के रूप में बाँध सकता है, भले ही दूसरे सॉकेट में यह विकल्प न हो। जब यह बाध्य था सेट करें । यह व्यवहार कुछ खतरनाक है क्योंकि यह किसी एप्लिकेशन को किसी अन्य एप्लिकेशन के कनेक्ट किए गए पोर्ट को "चोरी" करने की अनुमति देता है। कहने की जरूरत नहीं है, इसके बड़े सुरक्षा निहितार्थ हो सकते हैं। Microsoft ने महसूस किया कि यह एक समस्या हो सकती है और इस तरह एक और सॉकेट विकल्प जोड़ा गया SO_EXCLUSIVEADDRUSE। स्थापनाSO_EXCLUSIVEADDRUSEसॉकेट पर यह सुनिश्चित होता है कि यदि बंधन सफल हो जाता है, तो स्रोत पते और पोर्ट का संयोजन विशेष रूप से इस सॉकेट के स्वामित्व में होता है और कोई अन्य सॉकेट उन्हें बांध नहीं सकता है, भले ही यह SO_REUSEADDRसेट किया गया हो।

झंडे SO_REUSEADDRऔर SO_EXCLUSIVEADDRUSEविंडोज पर काम करने के तरीके के बारे में और अधिक जानकारी के लिए , वे कैसे बाध्यकारी / पुन: बाध्यकारी को प्रभावित करते हैं, Microsoft ने कृपया उस उत्तर के शीर्ष के पास मेरी तालिका के समान एक तालिका प्रदान की। बस इस पृष्ठ पर जाएँ और थोड़ा नीचे स्क्रॉल करें। वास्तव में तीन टेबल हैं, पहला वाला पुराने व्यवहार को दिखाता है (पूर्व विंडोज 2003), दूसरा एक व्यवहार (विंडोज 2003 और ऊपर) और तीसरा एक दिखाता है कि विंडोज 2003 में और बाद में bind()कॉल कैसे बदलते हैं। विभिन्न उपयोगकर्ताओं।


सोलारिस

सोलारिस सनोस का उत्तराधिकारी है। SunOS मूल रूप से BSD के एक कांटे पर आधारित था, SunOS 5 और बाद में SVR4 के एक कांटे पर आधारित था, हालांकि SVR4 BSD, System V और Xenix का विलय है, इसलिए कुछ हद तक Solaris भी BSD कांटा है, और a बल्कि जल्दी। परिणामस्वरूप सोलारिस केवल जानता है SO_REUSEADDR, कोई भी नहीं है SO_REUSEPORTSO_REUSEADDRबर्ताव बहुत ज्यादा एक ही रूप में यह बीएसडी में करता है। जहां तक ​​मुझे पता है SO_REUSEPORTकि सोलारिस के समान व्यवहार प्राप्त करने का कोई तरीका नहीं है , तो इसका मतलब है कि एक ही पते और पोर्ट के लिए दो सॉकेट को बांधना संभव नहीं है।

विंडोज के समान, सोलारिस में सॉकेट को एक विशेष बंधन देने का विकल्प होता है। इस विकल्प का नाम है SO_EXCLBIND। यदि यह विकल्प एक सॉकेट पर इसे बांधने से पहले सेट किया गया है, तो SO_REUSEADDRदूसरे सॉकेट पर सेटिंग का कोई प्रभाव नहीं पड़ता है यदि दो सॉकेट्स का पता विरोध संघर्ष के लिए लगाया जाता है। उदाहरण के लिए यदि socketAकिसी वाइल्ड कार्ड का पता करने के लिए बाध्य है और socketBहै SO_REUSEADDRसक्षम है और एक गैर वाइल्डकार्ड पता और के रूप में ही बंदरगाह के लिए बाध्य है socketA, इस बाँध सामान्य रूप से सफल होगा, जब तक socketAथा SO_EXCLBINDसक्षम है, जो मामले में यह भले ही विफल हो जाएगा SO_REUSEADDRका झंडा socketB


अन्य सिस्टम

यदि आपका सिस्टम ऊपर सूचीबद्ध नहीं है, तो मैंने एक छोटा परीक्षण कार्यक्रम लिखा, जिसका उपयोग करके आप यह पता लगा सकते हैं कि आपका सिस्टम इन दो विकल्पों को कैसे संभालता है। इसके अलावा अगर आपको लगता है कि मेरे परिणाम गलत हैं , तो कृपया किसी भी टिप्पणी को पोस्ट करने से पहले उस कार्यक्रम को चलाएं और संभवतः गलत दावे करें।

कोड का निर्माण करने के लिए सभी को थोड़ा पॉसिक्स एपीआई (नेटवर्क के पुर्जों के लिए) और एक सी 99 कंपाइलर (वास्तव में सबसे गैर-सी 99 कंपाइलर के रूप में अच्छी तरह से जब तक वे पेशकश करते हैं inttypes.hऔर काम करेंगे stdbool.h; उदाहरण के लिए gccपूर्ण सी 99 सपोर्ट देने से पहले दोनों का समर्थन किया गया) ।

प्रोग्राम को चलाने के लिए आवश्यक है कि आपके सिस्टम में कम से कम एक इंटरफ़ेस (स्थानीय इंटरफ़ेस के अलावा) में एक IP पता असाइन किया गया हो और एक डिफ़ॉल्ट मार्ग सेट किया गया हो जो उस इंटरफ़ेस का उपयोग करता हो। कार्यक्रम उस आईपी पते को इकट्ठा करेगा और इसे दूसरे "विशिष्ट पते" के रूप में उपयोग करेगा।

यह उन सभी संभावित संयोजनों का परीक्षण करता है जिनके बारे में आप सोच सकते हैं:

  • टीसीपी और यूडीपी प्रोटोकॉल
  • सामान्य सॉकेट, सुनो (सर्वर) सॉकेट्स, मल्टीकास्ट सॉकेट्स
  • SO_REUSEADDR सॉकेट 1, सॉकेट 2, या दोनों सॉकेट्स पर सेट करें
  • SO_REUSEPORT सॉकेट 1, सॉकेट 2, या दोनों सॉकेट्स पर सेट करें
  • सभी पते के संयोजन आप 0.0.0.0(वाइल्डकार्ड), 127.0.0.1(विशिष्ट पता), और दूसरा विशिष्ट पता अपने प्राथमिक इंटरफ़ेस पर प्राप्त कर सकते हैं (मल्टीकास्ट के लिए यह 224.1.2.3सभी परीक्षणों में है)

और एक अच्छी तालिका में परिणाम प्रिंट करता है। यह उन प्रणालियों पर भी काम करेगा जो नहीं जानते हैं SO_REUSEPORT, इस स्थिति में यह विकल्प बस परीक्षण नहीं किया जाता है।

जो कार्यक्रम आसानी से परीक्षण नहीं कर सकता है वह यह है कि राज्य SO_REUSEADDRमें सॉकेट्स पर कार्य कैसे होता TIME_WAITहै क्योंकि यह उस राज्य में सॉकेट को मजबूर करने और रखने के लिए बहुत मुश्किल है। सौभाग्य से अधिकांश ऑपरेटिंग सिस्टम बस बीएसडी की तरह व्यवहार करते हैं और अधिकांश समय प्रोग्रामर बस उस राज्य के अस्तित्व की अनदेखी कर सकते हैं।

यहां कोड है (मैं इसे यहां शामिल नहीं कर सकता, जवाबों की एक आकार सीमा होती है और कोड इस उत्तर को सीमा से अधिक धक्का देगा)।


9
उदाहरण के लिए, "स्रोत का पता" वास्तव में "स्थानीय पता" होना चाहिए, अगले तीन फ़ील्ड इसी तरह। बाइंडिंग INADDR_ANYमौजूदा स्थानीय पतों को नहीं बांधती, लेकिन भविष्य के सभी लोगों को भी। listenनिश्चित रूप से एक ही सटीक प्रोटोकॉल, स्थानीय पते और स्थानीय पोर्ट के साथ सॉकेट बनाता है, भले ही आपने कहा कि यह संभव नहीं है।
बेन वोइग्ट

9
@ बीन सोर्स और डेस्टिनेशन आईपी एड्रेसिंग के लिए इस्तेमाल होने वाले आधिकारिक शब्द हैं (जिस पर मैं प्राथमिक संदर्भ देता हूं)। स्थानीय और रिमोट का कोई मतलब नहीं होगा, क्योंकि दूरस्थ पता वास्तव में "स्थानीय" पता हो सकता है और गंतव्य के विपरीत स्रोत है और स्थानीय नहीं है। मुझे नहीं पता कि आपका मुद्दा क्या है INADDR_ANY, मैंने कभी नहीं कहा कि यह भविष्य के पतों पर नहीं होगा। और listenकोई भी सॉकेट नहीं बनाता है, जो आपके पूरे वाक्य को थोड़ा अजीब बनाता है।
मैकी

7
@ जब सिस्टम में एक नया पता जोड़ा जाता है, तो यह एक "मौजूदा स्थानीय पता" भी है, यह अभी अस्तित्व में है। मैंने यह नहीं कहा कि " वर्तमान में मौजूद सभी स्थानीय पते"। वास्तव में मैं यह भी कहता हूं कि सॉकेट वास्तव में वाइल्डकार्ड के लिए बाध्य है , जिसका अर्थ है कि सॉकेट जो कुछ भी इस वाइल्डकार्ड से मेल खाता है, अब, कल और सौ वर्षों में होता है। स्रोत और गंतव्य के लिए समान है, आप यहां केवल नाइटपैकिंग कर रहे हैं। क्या आपके पास बनाने के लिए कोई वास्तविक तकनीकी योगदान है?
मकेई

8
@ मीकी: आप वास्तव में सोचते हैं कि मौजूदा शब्द में ऐसी चीजें शामिल हैं जो अब मौजूद नहीं हैं लेकिन भविष्य में होंगी? स्रोत और गंतव्य एक नाइटपिक नहीं है। जब आने वाले पैकेट एक सॉकेट से मेल खाते हैं, तो आप कह रहे हैं कि पैकेट में गंतव्य पते को सॉकेट के "स्रोत" पते से मिलान किया जाएगा? यह गलत है और आप इसे जानते हैं, आपने पहले ही कहा था कि स्रोत और गंतव्य विपरीत हैं। स्थानीय सॉकेट पर पता के खिलाफ मिलान किया जाता है गंतव्य पता आने वाले पैकेट की, और में रखा स्रोत बाहर जाने वाले पैकेट पर पता।
बेन वोइगट

10
@ मेकी: यदि आप कहते हैं कि "सॉकेट का स्थानीय पता आउटगोइंग पैकेट का स्रोत पता और आने वाले पैकेट का गंतव्य पता है" तो यह बहुत अधिक समझ में आता है। पैकेट में स्रोत और गंतव्य पते हैं। मेजबान, और मेजबानों पर कुर्सियां, नहीं। डेटाग्राम सॉकेट के लिए दोनों सहकर्मी समान हैं। टीसीपी सॉकेट्स के लिए, तीन-तरफ़ा हैंडशेक के कारण, एक प्रवर्तक (क्लाइंट) और एक उत्तरदाता (सर्वर) है, लेकिन इसका मतलब यह नहीं है कि कनेक्शन या कनेक्टेड सॉकेट्स के पास स्रोत और गंतव्य दोनों हैं, क्योंकि ट्रैफ़िक दोनों तरह से बहता है।
बेन वोइगट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.