2013-01-24 56 views
5

मैं एसिंक्रोनस I/O का शोषण करने के लिए पूर्णता बंदरगाहों का उपयोग कर विंडोज एनटी में एक टीसीपी सर्वर लिख रहा हूं। मेरे पास एक टीसीपी सॉकेट क्लास है, एक टीसीपी सर्वर क्लास और कुछ (आभासी फ़ंक्शंस) कॉलबैक कॉल करने के लिए कॉल करते हैं जब I/O ऑपरेशन पूरा हो जाता है, उदा। ऑन रीड() जब एक पठन पूरा हो जाता है। जब कनेक्शन बंद हो जाता है, और इसी तरह कनेक्शन के लिए कनेक्शन चालू होता है और() पर भी ओपन() चालू होता है। मेरे पास सॉकेट के लिए हमेशा लंबित पढ़ा जाता है, इसलिए यदि सॉकेट प्रभावी रूप से डेटा प्राप्त करता है (पढ़ना आकार> 0 के साथ पूरा हो जाएगा) तो यह रेड() पर कॉल करता है, इसके बजाय यदि ग्राहक क्लाइंट साइड से सॉकेट बंद करता है (पढ़ना आकार == 0 के साथ पूरा हो जाएगा) यह Eof() पर कॉल करता है, और सर्वर को पता है कि जब ग्राहक सॉकेट को closesocket (server_socket) से बंद करता है; इसके पक्ष से।कुख्यात `ERROR_NETNAME_DELETED 'त्रुटि को एक त्रुटि माना जा सकता है?

सभी शान से काम करता है, लेकिन मैं एक बात पर ध्यान दिया है:

जब मैं closesocket (client_socket) कहते हैं; कनेक्शन के सर्वर के साइड एंडपॉइंट पर, क्लाइंट साइड की बजाय, (या तो अदरक {सत्य, 0} या नहीं) सेट करने के साथ, लंबित पढ़ना गलत, के रूप में पूरा हो जाएगा, जो पढ़ा गया आकार न केवल = = 0, लेकिन GetLastError() भी एक त्रुटि देता है: 64, या 'ERROR_NETNAME_DELETED'। मैंने वेब पर इस बारे में बहुत कुछ खोजा है, लेकिन कुछ भी दिलचस्प नहीं मिला।

फिर मैंने खुद से पूछा: लेकिन क्या यह एक वास्तविक त्रुटि है? मेरा मतलब है, क्या यह वास्तव में एक त्रुटि माना जा सकता है?

समस्या यह है कि सर्वर की तरफ, ऑनरर() कॉलबैक कॉल किया जाएगा जब मैं closesocket (client_socket); onEof() के बजाय। तो मैंने यह सोचा:

क्या होगा, जब यह 'ERROR_NETNAME_DELETED' "त्रुटि" प्राप्त होती है, तो Err() के बजाय Eof() पर कॉल करें? क्या यह कुछ बग या अपरिभाषित व्यवहार पेश करेगा? एक अन्य महत्वपूर्ण बिंदु है कि मुझे यह सवाल पूछते बना यह है:

जब मैं इस के साथ 'ERROR_NETNAME_DELETED' पूरा होने पढ़ प्राप्त हुआ है, मैं विशेष रूप से overlapped-> आंतरिक पैरामीटर जो NTSTATUS त्रुटि हो ओवरलैप संरचना जाँच कर ली है, अंतर्निहित ड्राइवर का कोड । अगर हम NTSTATUS त्रुटि कोड [http://www.tenox.tc/links/ntstatus.html] की एक सूची देखते हैं तो हम स्पष्ट रूप से देख सकते हैं कि 'ERROR_NETNAME_DELETED' NTSTATUS 0xC000013B द्वारा उत्पन्न होता है, जो एक त्रुटि है, लेकिन इसे 'STATUS_LOCAL_DISCONNECT' कहा जाता है। खैर, यह किसी त्रुटि के लिए नाम की तरह नहीं दिखता है। ऐसा लगता है कि 'ERROR_IO_PENDING' जैसी एक त्रुटि है, लेकिन एक सही व्यवहार की स्थिति भी है।

तो ओवरप्लेड संरचना के आंतरिक पैरामीटर की जांच करने के बारे में क्या, और जब यह == को 'STATUS_LOCAL_DISCONNECT' पर कॉल करने के लिए ऑनऑफ() कॉलबैक पर कॉल किया जाता है? क्या गड़बड़ चीजें होगी?

इसके अलावा, मुझे यह कहना है कि सर्वर पक्ष से, यदि मैं कोठरी (क्लाइंट_सेट) को कॉल करने से पहले DisconnectEx() को कॉल करता हूं; मुझे वह त्रुटि नहीं मिलेगी। लेकिन क्या मैं DisconnectEx() को कॉल नहीं करना चाहता हूं? जैसे जब सर्वर बंद हो रहा है और सभी DisconnectEx() पूर्णता का इंतजार नहीं करना चाहता, लेकिन बस सभी क्लाइंट के कनेक्ट को बंद करना चाहते हैं।

+1

@ हंस, मुझे लगता है कि उन्होंने यह वर्णन करने का एक अच्छा काम किया कि उन्हें उस त्रुटि का सामना कैसे हुआ। –

उत्तर

3

यह पूरी तरह से आप पर निर्भर करता है कि आप एक त्रुटि स्थिति का इलाज कैसे करते हैं। आपके मामले में यह त्रुटि स्थिति पूरी तरह से अपेक्षित है, और यह आपके लिए एक अपेक्षाकृत शर्त के रूप में इलाज करने के लिए पूरी तरह से सुरक्षित है।

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

+0

मैं आपसे सहमत हूं। एकमात्र कमी जिसे मैं कल्पना कर सकता हूं, यह है कि यदि त्रुटि त्रुटि ERROR_NETNAME_DELETED Windows में अन्य चीजों के लिए जेनरेट की गई है, और फिर जब वास्तविक त्रुटि स्थिति होती है तो onEof() कॉलबैक को ऑनर ​​() के बजाय बुलाया जाएगा। तो शायद मैं ओवरलैप्ड संरचना में एनटीस्टैटस की जांच कर सकता हूं। –

+0

आप 'ओवरलैप्टेड' संरचना से 'NTSTATUS' मान को पढ़ने के लिए नहीं हैं। यह आंतरिक है, और परिवर्तन के अधीन है। दस्तावेज उस पर स्पष्ट है। –

+0

हाँ, यह सही है, आंतरिक सदस्य का उपयोग करने के लिए नहीं है, इसलिए शायद मैं GetLastError() –

2

यह आपके ऊपर है कि त्रुटि की स्थिति का इलाज कैसे करें, लेकिन प्रश्न आपके कोड में संभावित समस्याओं का संकेत है (तर्क त्रुटियों से अपरिभाषित व्यवहार तक)।

सबसे महत्वपूर्ण बात यह है कि आपको closesocket के बाद सॉकेट हैंडल को स्पर्श नहीं करना चाहिए। ईओएफ पर क्या करते हैं? जब हम ईओएफ का पता लगाते हैं तो यह closesocket पर तार्किक होगा, लेकिन ERROR_NETNAME_DELETED हैंडलर में आप ऐसा नहीं कर सकते हैं, क्योंकि closesocket पहले से ही हुआ है और हैंडल अमान्य है।

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

क्या होता है यदि लंबित पढ़ना उस दुर्भाग्यपूर्ण क्षण में ईओएफ के साथ पूरा हो जाता है, बस closesocket से पहले? यदि आपका नियमित OnEof कॉलबैक निकाल दिया गया है, और वह कॉलबैक closesocket करता है, तो यह फिर से गलत होगा।

आपके द्वारा वर्णित समस्या अधिक गंभीर समस्या पर संकेत दे सकती है यदि closesocket एक थ्रेड में किया जाता है, जबकि दूसरा धागा I/O पूरा होने की प्रतीक्षा करता है। क्या आप सुनिश्चित हैं कि कोई अन्य थ्रेड WSARecv/ReadFile पर कॉल नहीं कर रहा है जबकि पहला धागा closesocket पर कॉल कर रहा है? यह अनिर्धारित व्यवहार है, भले ही विंसॉक ऐसा लगता है कि यह ज्यादातर समय काम करता है।

संक्षेप में, कोड हैंडलिंग को पूरा करने (या असफल) पढ़ने के लिए कोड सही नहीं हो सकता है अगर यह सॉकेट हैंडल से बेकार है क्योंकि यह बंद था। closesocket के बाद, लंबित I/O पूरा होने की प्रतीक्षा करना उपयोगी है क्योंकि यदि आप नहीं करते हैं तो आप OVERLAPPED संरचना का पुन: उपयोग नहीं कर सकते हैं; लेकिन को को पूरा करने में कोई बात नहीं है जैसे कि यह सामान्य ऑपरेशन के दौरान हुआ, सॉकेट अभी भी खुला है (त्रुटि/स्थिति कोड अप्रासंगिक है)।

+0

पर भरोसा करूँगा आपके पास वास्तव में एक अच्छा बिंदु है। खैर, मूल रूप से मेरे पास कुछ() dealloc स्मृति और इतने पर) को चलाने के लिए कुछ() dealloc स्मृति और इतने पर) चलाने के लिए है (लेकिन मुझे प्रभावी रूप से इसके लिए 2 कॉलबैक होना चाहिए: onEof() जिसे केवल तब कहा जाता है जब दूसरी तरफ कनेक्शन बंद हो जाता है, और ऑनक्लोज़() - इस तरह से जब onof() प्राप्त होता है, तो दूसरा भाग आपके द्वारा सुझाए गए अनुसार closesocket() को कॉल कर सकता है।इसलिए, अगर मुझे यह मिल गया, अगर लंबित पढ़ना बंद हो जाता है तो बंद करें() और एप्लिकेशन उन्हें बाद में पहचानता है, इस परिदृश्य को सिग्नल करने के लिए ERROR_NETNAME_DELETED है, और क्या इसका उद्देश्य है? –

+0

नहीं, "पहले पूरा हो गया है"/"पता चला है" परिदृश्य में 'ERROR_NETNAME_DELETED' नहीं है, यह एक उदाहरण है जहां चीजें * अनजान * तरीके से * तर्कसंगत * गलत हो सकती हैं। यह भी एक उदाहरण है कि 'ERROR_NETNAME_DELETED' कॉलबैक में क्लीनअप करना गलत क्यों है: सफल" पढ़ने के लिए इस "दुर्भाग्यपूर्ण" समय के साथ, कुछ भी संभालने के लिए कुछ भी नहीं है (और वहां * कुछ भी संभालने के लिए कुछ भी नहीं होगा क्योंकि आप 'WSARecv को फिर से निर्धारित नहीं कर सकते बंद सॉकेट पर)। –

0

आप गलत विधि कॉल कर रहे हैं। आपको WSAGetLastError() पर कॉल करना चाहिए। विंसॉक एपीआई कॉल के बाद GetLastError() का नतीजा व्यर्थ है।

+0

वास्तव में मैं WSAGetLastError() को कॉल कर रहा हूं। और इसके साथ ही, त्रुटि मान समान है। मैंने GetLastError() का उल्लेख किया है क्योंकि मैं गैर-नेटवर्क I/O के लिए I/O समापन बंदरगाहों का भी उपयोग करता हूं, लेकिन मुझे लगता है कि त्रुटि केवल नेटवर्क I/O के लिए समझ में आता है। –

+0

@MarcoPagliaricci तो वास्तव में आपके प्रश्न को ऐसा कहना चाहिए। – EJP