12

जब आप एक बंद टीसीपी सॉकेट पढ़ते हैं तो आपको नियमित त्रुटि मिलती है, यानी यह या तो ईओएफ या -1 इंगित करने वाला 0 देता है और errno में एक त्रुटि कोड जिसे perror के साथ मुद्रित किया जा सकता है।एक बंद टीसीपी सॉकेट लिखने से भी बदतर क्यों है?

हालांकि, जब आप एक बंद टीसीपी सॉकेट लिखते हैं तो ओएस आपके ऐप पर SIGPIPE भेजता है जो पकड़ा नहीं जाता है तो ऐप को समाप्त कर देगा।

बंद टीसीपी सॉकेट लिखने से भी बदतर क्यों लिख रहा है?

+0

यहां कुछ और चल रहा है जो काफी सूक्ष्म है: एक टीसीपी कनेक्शन आधा बंद हो सकता है, जिसका अर्थ है कि एक तरफ ने सॉकेट बंद कर दिया है (एक एफआईएन पैकेट भेजा है), लेकिन दूसरी तरफ अभी भी डेटा भेजने के लिए है। यदि आप इस स्तर पर चारों ओर घूम रहे हैं, तो कृपया पढ़ें: http://superuser.com/questions/298919/what-is-tcp-half-open-connection-and-tcp-half-closed-connection – rbp

उत्तर

12

+1 Greg Hewgill उत्तर देने के लिए सही दिशा में मेरी विचार प्रक्रिया को आगे बढ़ाने के लिए।

दोनों सॉकेट और पाइप में SIGPIPE का असली कारण फिल्टर idiom/पैटर्न है जो यूनिक्स सिस्टम में सामान्य I/O पर लागू होता है।

पाइप से शुरू हो रहा है। Grep जैसे फ़िल्टर प्रोग्राम आमतौर पर STDOUT पर लिखते हैं और STDIN से पढ़ते हैं, जिन्हें शैल द्वारा पाइप पर रीडायरेक्ट किया जा सकता है। उदाहरण के लिए:

cat someVeryBigFile | grep foo | doSomeThingErrorProne 

खोल जब यह कांटे और उसके बाद कार्यकारी के इन कार्यक्रमों शायद dup2 सिस्टम कॉल का उपयोग करता रीडायरेक्ट करने के लिए STDIN, STDOUT और STDERR उचित पाइप के लिए।

के बाद से फिल्टर कार्यक्रम grep पता नहीं है और जानते हुए भी कि यह उत्पादन फिर अगर doSomeThingErrorProne दुर्घटनाओं की वापसी मान के बाद से एक संकेत के साथ है एक टूटी हुई पाइप के लिए लिख रोकने के लिए यह बताने के लिए एक ही रास्ता रीडायरेक्ट किया गया है का कोई रास्ता नहीं है STDOUT पर लिखते हैं शायद ही कभी चेक किए जाते हैं।

सॉकेट के साथ एनालॉग inetd सर्वर खोल खोलने वाला सर्वर होगा।

उदाहरण के तौर पर मुझे लगता है कि आप grep को नेटवर्क सेवा में बदल सकते हैं जो TCP सॉकेट से अधिक संचालित करता है।

grep  8000/tcp # grep server 

फिर /etc/inetd.conf से जोड़ें::

grep stream tcp nowait root /usr/bin/grep grep foo 

inetd को SIGHUP भेजें और पोर्ट से कनेक्ट inetd साथ उदाहरण के लिए आप TCP बंदरगाह पर एक grep सर्वर 8000 तो /etc/services में जोड़ना चाहते हैं, तो टेलनेट के साथ 8000। इससे inetd कांटा, STDIN, STDOUT और STDERR पर सॉकेट को डुप्लिकेट करना चाहिए और उसके बाद foo के साथ foo के साथ grep निष्पादित करना चाहिए। यदि आप टेलनेट grep में टाइपिंग लाइन शुरू करते हैं तो उन पंक्तियों को प्रतिबिंबित करेंगे जिनमें foo शामिल है।

अब उदाहरण के लिए STDOUT वास्तविक समय शेयरों के भाव की एक धारा लिखते हैं और STDIN पर आदेशों हो जाता है कि ticker नाम के एक कार्यक्रम के साथ टेलनेट बदलें।सूर्य माइक्रोसिस्टम्स के लिए उद्धरण प्राप्त करने के लिए पोर्ट 8000 के लिए कोई टेलनेट और "जावा शुरू करें" टाइप करें। फिर वे उठते हैं और दोपहर के भोजन के लिए जाते हैं। टेलनेट अस्पष्ट रूप से दुर्घटनाग्रस्त हो जाता है। यदि भेजने के लिए ticker भेजने के लिए हमेशा उद्धरण भेजना जारी रहेगा, कभी नहीं जानते कि दूसरी छोर पर प्रक्रिया क्रैश हो गई है, और सिस्टम संसाधनों को बेकार कर दिया गया है।

10

आमतौर पर यदि आप सॉकेट में लिख रहे हैं, तो आप दूसरे छोर को सुनने की उम्मीद करेंगे। यह एक टेलीफोन कॉल की तरह है - यदि आप बोल रहे हैं, तो आप उम्मीद नहीं करेंगे कि दूसरी पार्टी बस कॉल को लटकाएगी।

यदि आप सॉकेट से पढ़ रहे हैं, तो आप दूसरे छोर की उम्मीद कर रहे हैं (ए) आपको कुछ भेजते हैं, या (बी) सॉकेट बंद करें। स्थिति (बी) तब होगा जब आपने दूसरे छोर पर QUIT कमांड की तरह कुछ भेजा हो।

+0

लेकिन वह वास्तव में मुझे नहीं बताता कि क्यों 'लिखना' या 'भेजना' सिर्फ एक ही तरह से 'रीड' या 'आरईवीवी' की तरह एक त्रुटि वापस नहीं कर सकता है। सिर पर ऐप को 'सिगिपी' के साथ क्यों फेंक दो? ओएस द्वारा इतनी चरम प्रतिक्रिया के लिए कुछ गहरा कारण होना चाहिए। मान लें कि मुझे एक सॉकेट मिला है जिसे अभी 'आरएसटी' प्राप्त हुआ है। यदि मैं इसे पढ़ता हूं तो मुझे ईसीएनएनआरईएसईटीई के साथ -1 मिलता है, जब मैं लिखता हूं तो वही चीज़ क्यों नहीं मिलता? दोनों मामलों में मैं सहमतिजनक I/O में शामिल होने की उम्मीद कर रहा हूं और मुझे उम्मीद नहीं मिली। –

+6

@Robert: यूनिक्स पर पाइप इनपुट और आउटपुट के लिए सामान्य उपयोग केस ऐतिहासिक रूप से "फ़िल्टर" प्रोग्रामों के लिए था, जो इनपुट पाइप से पढ़ते हैं और आउटपुट पाइप को लिखते हैं ('grep' प्रोग्राम ऐसा उदाहरण है)। जब आउटपुट अब नहीं सुन रहा है तो ऐसा फ़िल्टर तुरंत समाप्त करने के लिए, 'SIGPIPE' सिग्नल डिफ़ॉल्ट व्यवहार प्रोग्राम को समाप्त करने के लिए सेट किया गया था। इसके बिना, जब तक इनपुट समाप्त नहीं हो जाता तब तक फ़िल्टर आउटपुट को लिखना जारी रखेगा (जो थोड़ी देर हो सकता है)। –

+2

मुझे बताएं कि यह सही लगता है: 'सिगिपी' के लिए वास्तविक कारण यह है कि grep जैसे फिल्टर प्रोग्राम आमतौर पर 'STDOUT' को लिखते हैं, जिसे खोल द्वारा पाइप पर रीडायरेक्ट किया जा सकता है। चूंकि फ़िल्टर प्रोग्राम नहीं जानता है और इसका यह जानने का कोई तरीका नहीं है कि इसका आउटपुट रीडायरेक्ट किया गया है, तो इसे टूटा हुआ पाइप पर लिखना बंद करने का एकमात्र तरीका सिग्नल के साथ है क्योंकि 'STDOUT' को लिखने के रिटर्न वैल्यू शायद ही कभी चेक किए जाते हैं । सॉकेट के साथ एनालॉग 'कनेक्शन' स्वीकार कर रहा है, सर्वर को घुमाएगा और 'एसटीडीआईएन', 'एसटीडीओटीटी', 'एसटीडीईआरआर' पर सॉकेट में डुप्लिकेट करेगा! –

3

मुझे लगता है कि उत्तर का एक बड़ा हिस्सा है 'ताकि एक सॉकेट क्लासिक यूनिक्स (अनाम) पाइप के समान व्यवहार करे। वे भी वही व्यवहार प्रदर्शित करते हैं - सिग्नल का नाम गवाह करते हैं।

तो, यह पूछना उचित है कि पाइप इस तरह से व्यवहार क्यों करते हैं। ग्रेग हेगिल का जवाब स्थिति का सारांश देता है।

इसे देखने का एक और तरीका है - विकल्प क्या है? क्या किसी लेखक के साथ एक पाइप पर 'पढ़ा()' एक सिगिप सिग्नल दे सकता है? SIGPIPE का अर्थ निश्चित रूप से 'इसे पढ़ने के लिए किसी भी पाइप पर लिखना' से बदलना होगा, लेकिन यह मामूली है। यह सोचने का कोई विशेष कारण नहीं है कि यह बेहतर होगा; ईओएफ संकेत (पढ़ने के लिए शून्य बाइट; शून्य बाइट्स पढ़ा जाता है) पाइप की स्थिति का एक सही वर्णन है, और इसलिए पढ़ने का व्यवहार अच्छा है।

'लिखने()' के बारे में क्या? खैर, एक विकल्प लिखा बाइट्स की संख्या वापस करने के लिए होगा - शून्य। लेकिन यह एक अच्छा विचार नहीं है; इसका तात्पर्य है कि कोड को फिर से प्रयास करना चाहिए और शायद अधिक बाइट भेजे जाएंगे, जो मामला नहीं होगा। एक और विकल्प एक त्रुटि होगी - लिखें() रिटर्न -1 और एक उपयुक्त त्रुटि सेट करता है। यह स्पष्ट नहीं है कि एक है। EINVAL या EBADF दोनों गलत हैं: फाइल डिस्क्रिप्टर इस अंत में सही और खुला है (और असफल लेखन के बाद बंद होना चाहिए); वहां पढ़ने के लिए कुछ भी नहीं है। ईपीआईपीई का अर्थ है 'टूटा हुआ पीआईपीई'; इसलिए, "यह एक सॉकेट है, एक पाइप नहीं है" के बारे में एक चेतावनी के साथ, यह उचित त्रुटि होगी। यदि आप सिगिप को अनदेखा करते हैं तो शायद यह इरनो वापस आ गया है। ऐसा करने के लिए यह संभव होगा - जब पाइप टूट जाती है (और संकेत कभी नहीं भेजें) तो एक उचित त्रुटि लौटाएं। हालांकि, यह एक अनुभवजन्य तथ्य है कि कई कार्यक्रम इस बात पर ज्यादा ध्यान नहीं देते हैं कि उनका आउटपुट कहां जा रहा है, और यदि आप एक कमांड पाइप करते हैं जो एक बहु-गीगाबाइट फ़ाइल को उस प्रक्रिया में पढ़ेगा जो पहले 20 केबी के बाद छोड़ देता है, लेकिन यह अपने लेखन की स्थिति पर ध्यान नहीं दे रहा है, तो इसे पूरा करने में काफी समय लगेगा, और ऐसा करने के दौरान मशीन प्रयास बर्बाद कर देगा, जबकि इसे एक संकेत भेजकर कि यह अनदेखा नहीं कर रहा है, यह जल्दी से बंद हो जाएगा - यह निश्चित रूप से फायदेमंद है। और अगर आप इसे चाहते हैं तो आप त्रुटि प्राप्त कर सकते हैं। इसलिए सिग्नल भेजने से पाइप के संदर्भ में ओ/एस को लाभ होता है; और सॉकेट पाइपों को बारीकी से अनुकरण करते हैं।

दिलचस्प एक तरफ: भेजने और प्राप्त करने की प्रक्रिया के बीच डेटा का एक बड़ा पाइप लाइन के रूप में सॉकेट की

#define SO_NOSIGPIPE 0x1022 /* APPLE: No SIGPIPE on EPIPE */ 
+0

तो मूल रूप से आप कह रहे हैं कि 'SIGPIPE' मौजूद है क्योंकि बहुत से प्रोग्रामर लिखने के मामले में त्रुटि कोड को अनदेखा करते हैं जो प्रक्रिया को संसाधनों को हॉग करने का कारण बन सकता है जब यह वास्तव में कुछ भी पूरा नहीं कर रहा है? या इसे एक और तरीके से रखने के लिए, लोग अपने आउटपुट की तुलना में अपने इनपुट की जांच करने के बारे में अधिक सावधानीपूर्वक हैं और यही कारण है कि 'पढ़ने' और 'लिखने' में असमानता का कारण है? –

+0

@Robert: हाँ, मूल रूप से। लोग इस धारणा पर अपना कोड लिखते हैं कि आउटपुट डिवाइस नहीं चलेगा, या अंतरिक्ष से बाहर नहीं होगा। जब आउटपुट एक पाइप होता है और आउटपुट प्रोग्राम आउटपुट के अंत से पहले पढ़ना बंद कर देता है, तो यह सुनिश्चित करना महत्वपूर्ण है कि लेखन कार्यक्रम ध्यान देता है। और यह एक साधारण तंत्र है जो लिखने के लिए प्रोग्राम को सरल बनाता है। –

+0

तो क्या ऐसा समय था जब प्री-डेटेड 'सिगिपिप'? चूंकि आप यह कह रहे हैं कि यह कुछ हद तक उपयोगकर्ता/प्रोग्रामर खराब व्यवहार का परिणाम है, वहां यूनिक्स के एक संस्करण के बाद एक बंद पाइप पर लिखते समय एक त्रुटि आई और फिर उन्होंने इसे एक सिग्नल वापस करने के लिए बदल दिया, या 'सिगिप' बुरे व्यवहार की प्रत्याशा में शुरुआत से ही? –

7

सोचें: SIGPIPE के लिए संदेश की जाँच करते समय, मैं सॉकेट विकल्प मिल गया। अब कल्पना करें कि पाइपलाइन में एक वाल्व है जो बंद है (सॉकेट कनेक्शन बंद है)।

यदि आप सॉकेट से पढ़ रहे हैं (पाइप से कुछ प्राप्त करने की कोशिश कर रहे हैं), वहां कुछ ऐसा पढ़ने की कोशिश करने में कोई हानि नहीं है; आपको बस कोई डेटा नहीं मिलेगा। वास्तव में, जैसा कि आपने कहा था, आप एक ईओएफ प्राप्त कर सकते हैं, जो सही है, क्योंकि पढ़ने के लिए कोई और डेटा नहीं है।

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

आप हमेशा सिग्नल को अनदेखा या अवरुद्ध कर सकते हैं, लेकिन आप अपने जोखिम पर ऐसा करते हैं।

संबंधित मुद्दे