2008-09-15 20 views
9

प्रलेखन के बावजूद, NetworkStream.Write डेटा भेजने के बाद तक प्रतीक्षा नहीं करता है। इसके बजाए, यह तब तक प्रतीक्षा करता है जब तक डेटा को बफर में कॉपी नहीं किया जाता है और फिर वापस आता है। वह बफर पृष्ठभूमि में प्रसारित होता है।नेटवर्कस्ट्रीम। राइट तुरंत लौटाता है - मैं डेटा कैसे भेजना समाप्त कर सकता हूं?

यह वह समय है जो मेरे पास है। चाहे मैं ns.Write या ns.BeginWrite का कोई फर्क नहीं पड़ता - दोनों तुरंत लौटते हैं। एंडवाइट भी तुरंत लौटता है (जो समझ में आता है क्योंकि यह प्रेषण बफर को लिख रहा है, नेटवर्क पर नहीं लिख रहा है)।

bool done; 
    void SendData(TcpClient tcp, byte[] data) 
    { 
     NetworkStream ns = tcp.GetStream(); 
     done = false; 
     ns.BeginWrite(bytWriteBuffer, 0, data.Length, myWriteCallBack, ns); 
     while (done == false) Thread.Sleep(10); 
    } 
      
    public void myWriteCallBack(IAsyncResult ar) 
    { 
     NetworkStream ns = (NetworkStream)ar.AsyncState; 
     ns.EndWrite(ar); 
     done = true; 
    } 

मैं कैसे बता सकता हूं कि डेटा वास्तव में ग्राहक को कब भेजा गया है?

मैं अपना डेटा भेजने के बाद सर्वर से प्रतिक्रिया के लिए 10 सेकंड (उदाहरण के लिए) का इंतजार करना चाहता हूं अन्यथा मुझे लगता है कि कुछ गलत था। यदि मेरा डेटा भेजने में 15 सेकंड लगते हैं, तो यह हमेशा समय-समय पर होगा क्योंकि मैं केवल तब से गिनती शुरू कर सकता हूं जब NetworkStream.Write रिटर्न - जो डेटा भेजने से पहले है। मैं अपने नेटवर्क कार्ड को छोड़ने के बाद से 10 सेकंड गिनना शुरू करना चाहता हूं।

डेटा भेजने की मात्रा और इसे भेजने का समय अलग-अलग हो सकता है - इसे भेजने के लिए 1 सेकंड लग सकते हैं, इसे भेजने में 10 सेकंड लग सकते हैं, इसे भेजने में एक मिनट लग सकता है। जब सर्वर डेटा प्राप्त करता है तो यह प्रतिक्रिया भेजता है (यह एक एसएमटीपी सर्वर है), लेकिन अगर मेरा डेटा खराब हो गया है और प्रतिक्रिया कभी नहीं आती है, तो मैं हमेशा के लिए इंतजार नहीं करना चाहता, इसलिए मुझे यह जानने की जरूरत है कि मैं ' मैं डेटा भेजने के लिए इंतजार कर रहा हूं, या अगर मैं सर्वर का जवाब देने का इंतजार कर रहा हूं।

मैं उपयोगकर्ता को स्थिति दिखाना चाहता हूं - मैं "सर्वर पर डेटा भेजना" और "सर्वर से प्रतिक्रिया की प्रतीक्षा करना" दिखाना चाहता हूं - मैं यह कैसे कर सकता हूं?

+0

क्या आपने 100 एमबी पैकेट के साथ NetworkStream.Write को आजमाया है? – Calmarius

उत्तर

0

फ्लश() विधि का उपयोग करने के बारे में कैसे।

ns.Flush() 

यह सुनिश्चित करना चाहिए कि डेटा जारी रखने से पहले लिखा गया हो।

+1

फ्लश विधि Stream.Flush विधि लागू करता है; हालांकि, क्योंकि नेटवर्कस्ट्रीम buffered नहीं है, यह नेटवर्क धाराओं पर कोई प्रभाव नहीं पड़ता है। फ़्लश विधि को कॉल करना अपवाद नहीं फेंकता है। http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.flush.aspx – GEOCHET

2

सामान्य रूप से, मैं वैसे भी ग्राहक से एक पावती भेजने की अनुशंसा करता हूं। इस तरह से आप 100% सुनिश्चित कर सकते हैं कि डेटा प्राप्त हुआ था, और सही ढंग से प्राप्त हुआ।

1

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

+0

दरअसल, एक सीएमडी/एसीके वह प्राप्त करने का एकमात्र तरीका है जो वह चाहता है। – GEOCHET

-1

शायद स्थापित करने tcp.NoDelay = true

+0

यह नागल का उपयोग न करने के लिए कहता है। नागल वह जगह है जहां यह किसी भी डेटा भेजने से पहले ~ 200ms की प्रतीक्षा करता है।विचार यह है कि थोड़ा इंतजार यह सुनिश्चित करता है कि यह जितना संभव हो उतना डेटा भेजने जा रहा है, जिससे नेटवर्क उपयोग कम हो जाएगा। आपको अभी भी पता नहीं है कि डेटा कब भेजा गया है। –

5

टीसीपी एक "विश्वसनीय" प्रोटोकॉल है, जो डेटा दूसरे छोर पर प्राप्त हो जाएगा मतलब है अगर कोई सॉकेट त्रुटियाँ हैं है की कोशिश करो। मैंने उच्च स्तरीय आवेदन पुष्टिकरण के साथ दूसरी अनुमानित टीसीपी में कई प्रयास किए हैं, लेकिन आईएमएचओ आमतौर पर समय और बैंडविड्थ का अपशिष्ट होता है।

आमतौर पर समस्या आपके द्वारा बताई गई सामान्य क्लाइंट/सर्वर डिजाइन, जो अपने सरलतम रूप में इस प्रकार है के माध्यम से नियंत्रित किया जाता है ...

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

एक बार जब सर्वर ने अनुरोध संसाधित कर लिया है और प्रतिक्रिया भेजी है तो आमतौर पर यह नहीं होता कि क्या होता है - भले ही सॉकेट लेनदेन के दौरान चली जाती है - क्योंकि यह किसी भी आगे की बातचीत शुरू करने के लिए ग्राहक पर निर्भर करता है। निजी तौर पर, मुझे सर्वर होने के लिए यह बहुत ही आरामदायक लगता है। :-)

1

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

यहां आपके मूल संस्करण की तुलना में कुछ क्लीनर कोड है, प्रतिनिधि, फ़ील्ड और थ्रेड को हटाएं। सो जाओ। यह कार्यप्रणाली के समान तरीके से पूर्ववत करता है।

void SendData(TcpClient tcp, byte[] data) { 
    NetworkStream ns = tcp.GetStream(); 
    // BUG?: should bytWriteBuffer == data? 
    IAsyncResult r = ns.BeginWrite(bytWriteBuffer, 0, data.Length, null, null); 
    r.AsyncWaitHandle.WaitOne(); 
    ns.EndWrite(r); 
} 

ऐसा लगता है कि ऊपर लिखा गया था जब प्रश्न संशोधित किया गया था। .WaitOne() आपके टाइमआउट मुद्दे की सहायता कर सकता है। इसे एक टाइमआउट पैरामीटर पारित किया जा सकता है। यह एक आलसी इंतजार है - परिणाम समाप्त होने तक थ्रेड को फिर से निर्धारित नहीं किया जाएगा, या टाइमआउट समाप्त हो जाएगा।

1

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

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

कुल मिलाकर, स्रोत फ़ाइलों के अंदर एक टाइमआउट मान हार्ड कोड के लिए यह बुरा अभ्यास है। यदि टाइमआउट मान रनटाइम पर कॉन्फ़िगर करने योग्य है, तो सब कुछ ठीक काम करना चाहिए।

11

मैं सी # प्रोग्रामर नहीं हूं, लेकिन जिस तरह से आपने यह सवाल पूछा है वह थोड़ा भ्रामक है। "प्राप्त" की किसी भी उपयोगी परिभाषा के लिए, आपका डेटा "प्राप्त" होने पर जानने का एकमात्र तरीका यह है कि आपके प्रोटोकॉल में एक विशिष्ट पावती संदेश होना चाहिए जो इंगित करता है कि डेटा पूरी तरह संसाधित हो गया है।

डेटा आपके नेटवर्क कार्ड को "छोड़" नहीं देता है।सबसे अच्छा तरीका है नेटवर्क के लिए अपने कार्यक्रम के रिश्ते के बारे में सोचना है:

अपने कार्यक्रम -> भ्रामक सामान के बहुत सारे -> सहकर्मी कार्यक्रम

चीजें हैं जो "बहुत सारे में हो सकता है की एक सूची की भ्रामक सामान ":

  • CLR
  • ऑपरेटिंग सिस्टम कर्नेल
  • एक आभासी नेटवर्क इंटरफेस
  • एक स्विच
  • एक सॉफ्टवेयर फ़ायरवॉल
  • एक हार्डवेयर फ़ायरवॉल
  • एक रूटर प्रदर्शन नेटवर्क पता अनुवाद
  • सहकर्मी के अंत प्रदर्शन नेटवर्क पता अनुवाद पर एक रूटर

तो, अगर आप कर रहे हैं एक वर्चुअल मशीन पर, जिसे एक अलग ऑपरेटिंग सिस्टम के तहत होस्ट किया जाता है, जिसमें एक सॉफ्टवेयर फ़ायरवॉल है जो वर्चुअल मशीन के नेटवर्क व्यवहार को नियंत्रित कर रहा है - जब डेटा "वास्तव में" आपके नेटवर्क कार्ड को छोड़ देता है? यहां तक ​​कि सबसे अच्छे मामले परिदृश्य में, इनमें से कई घटक एक पैकेट छोड़ सकते हैं, जिसे आपके नेटवर्क कार्ड को फिर से प्रेषित करने की आवश्यकता होगी। क्या पहले (असफल) प्रयास किए जाने पर यह आपके नेटवर्क कार्ड को "छोड़ दिया" है? अधिकांश नेटवर्किंग एपीआई नहीं कहेंगे, इसे तब तक "भेजा नहीं गया" जब तक कि दूसरे छोर ने टीसीपी पावती नहीं भेजी हो।

जिसके अनुसार, the documentation for NetworkStream.Write इंगित करने के लिए जब तक यह कम से कम शुरू किया है कि यह नहीं लौटेगा 'भेजें' आपरेशन लगता है:

लिखें विधि ब्लॉक बाइट्स की अनुरोध की गई संख्या भेज दिया जाता है जब तक या एक SocketException है फेंक दिया।

बेशक, "भेजा गया है" ऊपर दिए गए कारणों के लिए कुछ अस्पष्ट है। यह भी संभावना है कि डेटा आपके कार्यक्रम द्वारा "वास्तव में" भेजा जाएगा और सहकर्मी कार्यक्रम द्वारा प्राप्त किया जाएगा, लेकिन सहकर्मी क्रैश होगा या अन्यथा वास्तव में डेटा को संसाधित नहीं करेगा। तो आपको Write पर एक संदेश के Read के बाद करना चाहिए जो वास्तव में संदेश को संसाधित करते समय आपके सहकर्मी द्वारा उत्सर्जित किया जाएगा।

+0

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

+0

टीसीपी एसीके का मतलब है कि रिमोट * ऑपरेटिंग सिस्टम * (रिमोट एप्लिकेशन नहीं) को आपका कुछ डेटा प्राप्त हुआ है। या संभवतः कुछ नेटवर्किंग मिडिल-बॉक्स जो आपके रिमोट सिस्टम की मदद के लिए टीसीपी बफरिंग कर रहा है। आप आवेदन डेटा की मजबूत डिलीवरी के लिए इस पर भरोसा नहीं कर सकते हैं। – Glyph

0

बोले .नेट विंडोज सॉकेट है जो टीसीपी का उपयोग करता है। टीसीपी प्रेषक को सूचित करने के लिए एसीके पैकेट का उपयोग करता है डेटा को सफलतापूर्वक स्थानांतरित कर दिया गया है। तो प्रेषक मशीन जानता है कि डेटा स्थानांतरित कर दिया गया है लेकिन उस जानकारी को .NET में प्राप्त करने के लिए कोई तरीका नहीं है (जिसे मैं जानता हूं)।

संपादित करें: बस एक विचार, कभी कोशिश नहीं की: लिखें() ब्लॉक केवल अगर सॉकेट बफर भरा हुआ है। तो अगर हम उस बफर आकार (SendBufferSize) को बहुत कम मूल्य (8? 1? 0?) तक कम करते हैं तो हम जो चाहते हैं उसे प्राप्त कर सकते हैं :)