2009-07-22 15 views
29

मैंने सॉकेट के बारे में SO पर कई संसाधन देखे हैं। मेरा मानना ​​है कि उनमें से कोई भी उन विवरणों को कवर नहीं करता जिन्हें मैं जानना चाहता था। मेरे आवेदन में, सर्वर सभी प्रसंस्करण करता है और ग्राहकों को आवधिक अद्यतन भेजता है।सी # में सॉकेट प्रोग्रामिंग के साथ शुरू करना - सर्वोत्तम प्रथाओं

इस पोस्ट का इरादा सॉकेट एप्लिकेशन विकसित करते समय आवश्यक सर्वोत्तम बुनियादी विचारों को कवर करना और सर्वोत्तम प्रथाओं पर चर्चा करना है। यहां मूलभूत चीजें हैं जिन्हें आप लगभग सभी सॉकेट आधारित अनुप्रयोगों के साथ देखेंगे।

1 - बाध्यकारी और एक सॉकेट

मैं निम्नलिखित कोड का उपयोग कर रहा पर सुन। यह मेरी मशीन पर अच्छी तरह से काम करता है। जब मैं इसे वास्तविक सर्वर पर तैनात करता हूं तो क्या मुझे किसी और चीज की देखभाल करने की ज़रूरत है?

IPHostEntry localHost = Dns.GetHostEntry(Dns.GetHostName()); 
IPEndPoint endPoint = new IPEndPoint(localHost.AddressList[0], 4444); 

serverSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, 
        ProtocolType.Tcp); 
serverSocket.Bind(endPoint); 
serverSocket.Listen(10); 

2 - प्राप्त डेटा

मैं एक 255 आकार बाइट सरणी का इस्तेमाल किया है। तो जब मुझे 255 बाइट्स से अधिक डेटा प्राप्त हो रहा है, तो मुझे पूरा डेटा प्राप्त होने तक प्राप्त विधि को कॉल करने की आवश्यकता है, है ना? एक बार मुझे पूरा डेटा मिलने के बाद, मुझे पूरा संदेश प्राप्त करने के लिए अब तक प्राप्त सभी बाइट्स को जोड़ना होगा। क्या वो सही है? या क्या एक बेहतर दृष्टिकोण है?

3 - डेटा भेजा जा रहा है और डेटा लंबाई निर्दिष्ट करने

चूंकि टीसीपी में कोई रास्ता नहीं है संदेश की लंबाई प्राप्त करने के लिए मिल रहा है, मैं संदेश के लिए लंबाई जोड़ने की योजना बना रहा हूँ। यह पैकेट का पहला बाइट होगा। इसलिए क्लाइंट सिस्टम जानता है कि पढ़ने के लिए कितना डेटा उपलब्ध है।

कोई अन्य बेहतर दृष्टिकोण?

4 - ग्राहक समापन

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

client.Shutdown(SocketShutdown.Both); 
client.Close(); 

कोई सुझाव या समस्याएं?

5 - सर्वर बंद

सर्वर सभी ग्राहकों के बंद का संकेत करने के लिए संदेश भेजता है। जब यह संदेश प्राप्त होता है तो प्रत्येक क्लाइंट सॉकेट को डिस्कनेक्ट कर देगा। ग्राहक सर्वर पर बंद संदेश भेज देंगे और बंद कर देंगे। एक बार जब सर्वर सभी ग्राहकों से घनिष्ठ संदेश प्राप्त करता है, तो यह सॉकेट को डिस्कनेक्ट करता है और सुनना बंद कर देता है। संसाधनों को जारी करने के लिए प्रत्येक ग्राहक सॉकेट पर पर कॉल करें। क्या यह सही दृष्टिकोण है?

6 - अज्ञात क्लाइंट डिस्कनेक्शन

कभी कभी, एक क्लाइंट सर्वर को सूचित किए बिना डिस्कनेक्ट कर सकते हैं। इसे संभालने की मेरी योजना यह है: जब सर्वर सभी ग्राहकों को संदेश भेजता है, तो सॉकेट स्थिति की जांच करें। यदि यह कनेक्ट नहीं है, तो उस क्लाइंट को क्लाइंट सूची से हटा दें और उस क्लाइंट के लिए सॉकेट बंद करें।

कोई भी मदद महान होगी!

+0

में इस विषय पर अधिक जानकारी प्राप्त कर सकते हैं मैं IPAddress के साथ मुद्दों खिड़कियों के कुछ पुराने संस्करण पर सुन लिया है। IPAddress ipAddress का उपयोग करना = नया आईपीएड्रेस (नया बाइट [] {0, 0, 0, 0}); मेरे लिए सबसे अच्छा प्रतीत होता है! – clamchoda

उत्तर

24

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

1 - बाइंडिंग और सुनने
आपका कोड मेरे लिए ठीक लग रहा है, व्यक्तिगत रूप से मैं का उपयोग करें:

serverSocket.Bind(new IPEndPoint(IPAddress.Any, 4444)); 

बजाय DNS मार्ग से जा रहा है, लेकिन मुझे नहीं लगता कि वहाँ एक वास्तविक समस्या या तो है मार्ग।

1.5 - स्वीकारना ग्राहक कनेक्शन
बस पूर्णता 'खातिर इस उल्लेख ... मैं आप यह कर रहे हैं अन्यथा आप 2.

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

मैं कहूंगा कि 1500 बाइट चुनना ठीक होना चाहिए, या शायद एक अच्छा दौर संख्या के लिए 2048 भी होना चाहिए।

वैकल्पिक रूप से, हो सकता है आप एक byte[] का उपयोग कर डेटा टुकड़े स्टोर करने के लिए और इसके बदले एक NetworkStream, एक BinaryReader में लिपटे में अपने सर्वर साइड ग्राहक सॉकेट लपेट, ताकि आप सॉकेट से सीधे कोई अपने संदेश के घटकों पढ़ सकते हैं बच सकते हैं बफर आकार के बारे में चिंता किए बिना।

3 - डेटा भेजा जा रहा है और डेटा लंबाई निर्दिष्ट करने
आपका दृष्टिकोण ठीक काम करेंगे, लेकिन यह स्पष्ट रूप से की आवश्यकता होती है है कि यह पैकेट की अवधि की गणना करने से पहले आप इसे भेजना शुरू आसान है।

वैकल्पिक रूप से, यदि आपका संदेश प्रारूप (इसके घटकों का क्रम) फैशन में डिज़ाइन किया गया है ताकि किसी भी समय ग्राहक यह निर्धारित करने में सक्षम हो कि क्या अधिक डेटा होना चाहिए (उदाहरण के लिए, कोड 0x01 का अर्थ है कि अगला होगा एक int और एक स्ट्रिंग, कोड 0x02 का मतलब है कि अगला 16 बाइट्स इत्यादि होगा)। ग्राहक पक्ष पर NetworkStream दृष्टिकोण के साथ संयुक्त, यह एक बहुत ही प्रभावी दृष्टिकोण हो सकता है।

सुरक्षित पक्ष पर रहने के लिए आप यह सुनिश्चित करने के लिए प्राप्त किए जा रहे घटकों के सत्यापन को जोड़ना चाहेंगे कि आप केवल सिन मानों को संसाधित करते हैं। उदाहरण के लिए, यदि आपको लंबाई 1TB की स्ट्रिंग के लिए संकेत मिलता है तो आपके पास कहीं पैकेट भ्रष्टाचार हो सकता है, और कनेक्शन को बंद करना और क्लाइंट को पुनः कनेक्ट करने और 'स्टार्ट ओवर' करने के लिए मजबूर होना सुरक्षित हो सकता है। अप्रत्याशित विफलताओं के मामले में यह दृष्टिकोण आपको बहुत अच्छा पकड़-सभी व्यवहार देता है।

4/5 - क्लाइंट और सर्वर बंद
निजी तौर पर मैं सिर्फ Close आगे संदेशों के बिना के लिए चुनते हैं; जब कोई कनेक्शन बंद हो जाता है तो आपको उस कनेक्शन के दूसरे छोर पर किसी भी अवरुद्ध पढ़ने/लिखने पर अपवाद मिलेगा जिसे आपको पूरा करना होगा।

चूंकि आपको एक मजबूत समाधान प्राप्त करने के लिए 'अज्ञात डिस्कनेक्शन' के लिए किसी भी तरह से पूरा करना है, इसलिए किसी भी जटिल को डिस्कनेक्ट करना आम तौर पर व्यर्थ है।

6 - अज्ञात डिस्कनेक्शन
मैं भी सॉकेट स्थिति पर भरोसा नहीं होगा ... यह एक कनेक्शन के बिना या तो क्लाइंट या सर्वर देख रही क्लाइंट/सर्वर के बीच मार्ग के किनारे कहीं मरने के लिए के लिए संभव है।

अप्रत्याशित रूप से मरने वाले कनेक्शन को बताने का एकमात्र गारंटी तरीका है जब आप अगली बार कनेक्शन के साथ कुछ भेजते हैं। उस बिंदु पर कनेक्शन के साथ कुछ भी गलत होने पर आपको हमेशा विफलता का संकेत मिलेगा।

नतीजतन, सभी अप्रत्याशित कनेक्शनों का पता लगाने का एकमात्र मूर्ख-प्रमाण तरीका एक 'पिंग' तंत्र को लागू करना है, जहां आदर्श रूप से ग्राहक और सर्वर समय-समय पर दूसरे संदेश को एक संदेश भेज देंगे जो केवल प्रतिक्रिया में परिणाम देगा संदेश इंगित करता है कि 'पिंग' प्राप्त हुआ था।

अनावश्यक पिंग्स को अनुकूलित करने के लिए, आप एक 'टाइम-आउट' तंत्र चाहते हैं जो केवल एक पिंग भेजता है जब किसी अन्य ट्रैफिक को एक निश्चित समय के लिए दूसरे छोर से प्राप्त नहीं किया जाता है (उदाहरण के लिए, यदि सर्वर से अंतिम संदेश x सेकंड से अधिक पुराना है, क्लाइंट यह सुनिश्चित करने के लिए एक पिंग भेजता है कि कनेक्शन के बिना कनेक्शन की मृत्यु नहीं हुई है)।

अधिक उन्नत
आप उच्च scalability आप सभी सॉकेट परिचालन (स्वीकार/भेजें/प्राप्त) के लिए अतुल्यकालिक तरीकों पर गौर करना होगा चाहते हैं। ये 'स्टार्ट/एंड' वेरिएंट हैं, लेकिन वे उपयोग करने के लिए बहुत जटिल हैं।

मैं इसे तब तक करने की सलाह देता हूं जब तक आपके पास सरल संस्करण और काम न हो।

यह भी ध्यान रखें कि यदि आप कुछ दर्जन से अधिक ग्राहकों को स्केल करने की योजना नहीं बना रहे हैं तो यह वास्तव में किसी समस्या के बावजूद समस्या नहीं होगी। Async तकनीक वास्तव में केवल तभी जरूरी है जब आप अपने सर्वर को पूरी तरह से मरने के दौरान हजारों या सैकड़ों हजारों कनेक्टेड क्लाइंट में स्केल करना चाहते हैं।

मैं शायद अन्य महत्वपूर्ण सुझाव की एक पूरी गुच्छा भूल गए हैं, लेकिन यह आप एक काफी मजबूत और विश्वसनीय कार्यान्वयन के साथ

+1

यह एक महान स्पष्टीकरण था। धन्यवाद –

+0

बस ध्यान रखें कि एक बार जब आप सब कुछ कर चुके हैं तो शायद यह विचार करने के लिए और अधिक चीजें हैं कि मैं उल्लेख करना भूल गया हूं ... – jerryjvl

13

1 शुरू करने के लिए करने के लिए पर्याप्त होना चाहिए - बाध्यकारी और एक सॉकेट पर सुन

मुझे ठीक लग रहा है। आपका कोड सॉकेट को केवल एक आईपी पते पर बांध देगा। आप बस किसी भी आईपी पते/नेटवर्क इंटरफेस पर सुनने के लिए चाहते हैं, IPAddress.Any का उपयोग करें:

serverSocket.Bind(new IPEndPoint(IPAddress.Any, 4444)); 

भविष्य सबूत होने के लिए, आप IPv6 का समर्थन कर सकते हैं। किसी भी आईपीवी 6 पते पर सुनने के लिए, IPAddress.Any के स्थान पर IPAddress.IPv6Any का उपयोग करें।

ध्यान दें कि आप एक ही समय में किसी भी आईपीवी 4 और किसी भी आईपीवी 6 पते पर नहीं सुन सकते हैं, सिवाय इसके कि यदि आप Dual-Stack Socket का उपयोग करते हैं।यह IPV6_V6ONLY सॉकेट विकल्प अनसेट करने की आवश्यकता होगी:

serverSocket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, 0); 

अपने सॉकेट के साथ Teredo सक्षम करने के लिए, आप PROTECTION_LEVEL_UNRESTRICTED सॉकेट विकल्प निर्धारित करने की आवश्यकता:

serverSocket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)23, 10); 

2 - प्राप्त डेटा

मैं NetworkStream का उपयोग करने की अनुशंसा करता हूं जो मैन्युअल रूप से भाग पढ़ने के बजाय Stream में सॉकेट को लपेटता है।

बाइट्स की एक निश्चित संख्या पढ़ना हालांकि थोड़ा अजीब है:

using (var stream = new NetworkStream(serverSocket)) { 
    var buffer = new byte[MaxMessageLength]; 
    while (true) { 
     int type = stream.ReadByte(); 
     if (type == BYE) break; 
     int length = stream.ReadByte(); 
     int offset = 0; 
     do 
     offset += stream.Read(buffer, offset, length - offset); 
     while (offset < length); 
     ProcessMessage(type, buffer, 0, length); 
    } 
} 

कहाँ NetworkStream वास्तव में चमकता है कि आप इसे किसी अन्य Stream की तरह उपयोग कर सकते हैं। यदि सुरक्षा महत्वपूर्ण है, तो सर्वर को प्रमाणित करने के लिए SslStream में NetworkStream को बस (और वैकल्पिक रूप से) X.50 9 प्रमाण पत्र वाले क्लाइंट को लपेटें। संपीड़न वैसे ही काम करता है।

var sslStream = new SslStream(stream, false); 
sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Tls, true); 
// receive/send data SSL secured 

3 - डेटा भेजा जा रहा है और डेटा लंबाई

आपका दृष्टिकोण से काम करना चाहिए निर्दिष्ट करने, हालांकि आप शायद पहिया पुनर्रचना करने के लिए सड़क के नीचे जाने के लिए और इस के लिए एक नए प्रोटोकॉल डिजाइन करने के लिए नहीं कर सकते हैं। BEEP पर एक नज़र डालें या शायद protobuf जैसे कुछ आसान भी देखें।

अपने लक्ष्यों के आधार पर, डब्ल्यूसीएफ या कुछ अन्य आरपीसी तंत्र जैसे सॉकेट के ऊपर एक अमूर्तता चुनने के बारे में सोचने लायक हो सकता है।

4/5/6 - समापन & अज्ञात डिस्कनेक्शन

क्या jerryjvl कहा :-) केवल विश्वसनीय पता लगाने तंत्र पिंग्स या भेजने रखें- Alives हैं जब कनेक्शन निष्क्रिय है।

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

+0

अल्थो यह चोट नहीं पहुंचा सकता है, मैंने पाया है कि अप्रत्याशित रूप से मजबूत वसूली पर ध्यान केंद्रित करना 'सुंदर डिस्कनेक्शन' संदेशों से बेहतर काम करता है ... वाईएमएमवी :) – jerryjvl

+0

ग्रेट पोस्ट। धन्यवाद। क्या आपके पास नेटवर्कस्ट्रीम का उपयोग करने के बारे में अधिक जानकारी है? कोई लिंक करेगा। –

+0

लिंक और उदाहरण उत्तर में जोड़ा गया। – dtb

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