2011-11-06 13 views
11

पढ़ा है मैंने एक साधारण टीसीपी क्लाइंट और सर्वर लिखा है। समस्या ग्राहक के साथ निहित है।लूप जब तक टीसीपी क्लाइंट प्रतिक्रिया पूरी तरह से

मुझे सर्वर से पूरी प्रतिक्रिया पढ़ने में कुछ परेशानी हो रही है। मुझे धागे को सभी डेटा भेजने की अनुमति देने के लिए सोना चाहिए।

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

// Init & connect to client 
TcpClient client = new TcpClient(); 
Console.WriteLine("Connecting....."); 
client.Connect("192.168.1.160", 9988); 

// Stream string to server 
input += "\n"; 
Stream stm = client.GetStream(); 
ASCIIEncoding asen = new ASCIIEncoding(); 
byte[] ba = asen.GetBytes(input); 
stm.Write(ba, 0, ba.Length); 

// Read response from server. 
byte[] buffer = new byte[1024]; 

System.Threading.Thread.Sleep(1000); // Huh, why do I need to wait? 

int bytesRead = stm.Read(buffer, 0, buffer.Length); 
response = Encoding.ASCII.GetString(buffer, 0, bytesRead); 
Console.WriteLine("Response String: "+response); 

client.Close(); 
+4

ग्राहक को समाप्त होने पर पता लगाने के लिए क्लाइंट को कुछ तरीका चाहिए (उदाहरण के लिए, विशेष समाप्ति अनुक्रम के लिए लंबाई उपसर्ग) – SLaks

+1

StreamReader http://msdn.microsoft.com/en-us/library/system का उपयोग करने का प्रयास करें। io.streamreader.aspx – David

+4

StreamReader कैसे मदद करेगा? यह नहीं जानता कि सर्वर कब समाप्त होता है। –

उत्तर

25

सॉकेट के शीर्ष पर बनाए गए धाराओं की प्रकृति यह है कि आपके पास खुली पाइपलाइन है जो सॉकेट बंद होने तक डेटा ट्रांसमिट और प्राप्त करती है।

हालांकि, क्लाइंट/सर्वर इंटरैक्शन की प्रकृति के कारण, इस पाइपलाइन को हमेशा पढ़ने के लिए सामग्री की गारंटी नहीं दी जाती है। ग्राहक और सर्वर को पाइपलाइन पर सामग्री भेजने के लिए सहमत होना है।

जब आप Stream abstraction in .NET लेते हैं और इसे सॉकेट की अवधारणा पर ओवरले करते हैं, तो ग्राहक और सर्वर के बीच एक समझौते की आवश्यकता अभी भी लागू होती है; आप Stream.Read पर कॉल कर सकते हैं, लेकिन यदि आपके Stream सॉकेट को दूसरी तरफ से कनेक्ट किया गया है, तो सामग्री भेज नहीं रही है, तो कॉल केवल सामग्री तक प्रतीक्षा करेगा।

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

  • एक लंबाई-उपसर्ग के संदेश जहां बाइट की संख्या पढ़ने के लिए भेज दिया जाता है से पहले संदेश
  • का अंत करने के लिए इस्तेमाल पात्रों में से एक पैटर्न संदेश (इस सामग्री है कि भेजा जा रहा है पर निर्भर करता है कम आम है, और अधिक मनमाना संदेश के किसी भी हिस्से, हो सकता है कम संभावना है इस का उपयोग किया जाएगा)

कहा है कि आप का पालन नहीं कर रहे हैं ऊपर; Stream.Read पर आपका कॉल केवल "बाइट्स पढ़ें" कह रहा है जब वास्तविकता में, 1024 बाइट पढ़ने के लिए नहीं हो सकता है। यदि ऐसा है, तो Stream.Read पर कॉल तब तक अवरुद्ध हो जाएगा जब तक कि यह पॉप्युलेट नहीं हो जाता है।

संभवतः Thread.Sleep पर कॉल करने का कारण संभवतः काम करता है क्योंकि एक बार दूसरी बार, Stream पर पढ़ने के लिए 1024 बाइट्स हैं और यह अवरुद्ध नहीं होता है।

इसके अतिरिक्त, यदि आप वास्तव में 1024 बाइट्स पढ़ना चाहते हैं, तो आप यह नहीं मान सकते कि Stream.Read पर कॉल 1024 बाइट डेटा पॉप्युलेट करेगा। Stream.Read विधि के लिए वापसी मूल्य आपको बताता है कि कितने बाइट वास्तव में पढ़े गए थे। अगर आपको अपने संदेश के लिए और अधिक चाहिए, तो आपको Stream.Read पर अतिरिक्त कॉल करने की आवश्यकता है।

Jon Skeet wrote up the exact way to do this यदि आप नमूना चाहते हैं।

+3

जॉन स्कीट का लेख शानदार है, धन्यवाद देने के लिए धन्यवाद। +1 –

+0

ऑब्जेक्ट deserialization लंबाई/अंत-संदेश संदेश समस्या का समाधान प्रदान करता है। उदाहरण के लिए प्रोटोबफ के साथ 'डिसेरियलाइज <कुछ मैसेज टाइप> (स्ट्रीम)' पूरी ऑब्जेक्ट होने तक स्वचालित रूप से पढ़ेगा। हुड के तहत, प्रोटोबफ के पास वस्तुओं के प्रारंभ/अंत का निर्धारण करने का अपना तरीका है। यह मानता है कि धारावाहिक पुस्तकालय धाराओं को ठीक से लागू करता है। फ़ाइल स्ट्रीम अक्सर बाइट्स (प्रदर्शन कारणों से) के पूर्ण बफर से भी कम लौटते हैं, इसलिए किसी भी उचित ढंग से लिखे गए पठन लूप को बाइट्स की वास्तविक संख्या को पढ़ना चाहिए और यह नहीं मानना ​​चाहिए कि पूर्ण बफर एक 'रेड' में भर गया था। – AaronLS

+0

एक सुधार, आपको http://stackoverflow.com/a/8901333/84206 – AaronLS

2

कोशिश दोहराने के लिए

int bytesRead = stm.Read(buffer, 0, buffer.Length); 

जबकि bytesRead> 0. यह उस के लिए एक आम पैटर्न के रूप में मुझे याद है। बेशक बफर के लिए उचित पैरा पास करना न भूलें।

0

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

0

आप जो डेटा पढ़ रहे हैं उसका आकार नहीं पता है, इसलिए आपको निर्णय लेने के लिए एक तंत्र स्थापित करना होगा। एक टाइमआउट है और दूसरा डिलीमीटर का उपयोग कर रहा है।

अपने उदाहरण पर आप केवल एक पुनरावृत्ति (पढ़ा) से जो भी डेटा पढ़ते हैं, क्योंकि आप "0" मिलीसेकंड के डिफ़ॉल्ट मान को पढ़ने और उपयोग करने के लिए टाइमआउट सेट नहीं करते हैं। तो आपको केवल 1000 एमएस सोना है। आप 1000 एमएस तक रिसीव समय का उपयोग करने के साथ एक ही प्रभाव प्राप्त करते हैं।

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

+0

क्या हमें वास्तव में इस" प्रारंभ/अंत चरित्र "को लागू करने की आवश्यकता है, या क्या .NET पहले ही इसे संभालता है? – Seva

+1

"स्टार्ट/एंड कैरेक्टर" कस्टम हैं इसलिए .NET ऐसी चीजों को लागू नहीं करता है। असल में हमने डेटा की लंबाई का उपयोग करके सिस्टम को अपनाना है क्योंकि संदेश उपसर्ग और सिस्टम सही तरीके से काम कर रहा है। लेकिन आपको मजबूत सॉकेट आर्किटेक्चर को लागू करना होगा। आप इस कोड का उपयोग कर सकते हैं http://stackoverflow.com/questions/869744/how-to-write-a-scalable-tcp-ip-based-server/20147995#20147995 –

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