2009-10-08 5 views
7

का उपयोग कर सर्वर डिज़ाइन मैं सॉकेटएसिंक EventArgs ईवेंट का उपयोग कर एक एसिंक्रोनस सॉकेट सर्वर बनाना चाहता हूं।SocketAsyncEventArgs

सर्वर को एक ही समय में लगभग 1000 कनेक्शन प्रबंधित करना चाहिए। प्रत्येक पैकेट के लिए तर्क को संभालने का सबसे अच्छा तरीका क्या है?

सर्वर डिज़ाइन this MSDN example पर आधारित है, इसलिए प्रत्येक सॉकेट में डेटा प्राप्त करने के लिए अपना स्वयं का सॉकेटएसिंसेन्ट आर्ट होगा।

  1. तर्क सामान क्या अंदर समारोह प्राप्त करते हैं। कोई ओवरहेड नहीं बनाया जाएगा, लेकिन चूंकि तर्क प्राप्त होने से पहले अगला ReceiveAsync() कॉल नहीं किया जाएगा, इसलिए सॉकेट से नया डेटा नहीं पढ़ा जा सकता है। मेरे लिए दो मुख्य प्रश्न हैं: यदि ग्राहक बहुत सारे डेटा भेजता है और तर्क प्रसंस्करण भारी है, तो सिस्टम इसे कैसे संभालेगा (पैकेट खो गया है क्योंकि बफर भरा हुआ है)? साथ ही, यदि सभी क्लाइंट एक ही समय में डेटा भेजते हैं, तो क्या 1000 थ्रेड होंगे, या क्या कोई आंतरिक सीमा है और कोई नया थ्रेड निष्पादन पूरा करने से पहले शुरू नहीं हो सकता है?

  2. एक कतार का उपयोग करें। प्राप्त समारोह बहुत छोटा होगा और तेजी से निष्पादित होगा, लेकिन कतार के कारण आपके पास सभ्य ओवरहेड होगा। समस्याएं हैं, यदि आपके कार्यकर्ता धागे भारी सर्वर लोड के तहत पर्याप्त तेज़ नहीं हैं, तो आपकी कतार पूर्ण हो सकती है, इसलिए शायद आपको पैकेट बूंदों को मजबूर करना होगा। आपको निर्माता/उपभोक्ता समस्या भी मिलती है, जो संभवतः पूरे कतार को कई ताले से धीमा कर सकती है।

तो क्या बेहतर डिजाइन हो जाएगा, तर्क में समारोह, तर्क कार्यकर्ता धागे या कुछ भी पूरी तरह से अलग मैं अब तक नहीं छूटा है में प्राप्त करते हैं।

डेटा भेजने के संबंध में एक और क्वेस्ट।

क्या सॉकेट एसिंसेन्ट आर्ट्स सॉकेट से जुड़ा हुआ है (प्राप्त करने के लिए एनालॉग) और कुछ छोटे पैकेट के लिए एक कॉल भेजने के लिए बफर सिस्टम का उपयोग करना बेहतर है (मान लें कि पैकेट अन्यथा कभी-कभी होंगे! सीधे बाद में भेजें एक और) या प्रत्येक पैकेट के लिए एक अलग सॉकेटएसिंसेन्ट आर्ट्स का उपयोग करें और उन्हें पूल में पुन: उपयोग करने के लिए स्टोर करें?

उत्तर

10

एसिंक सॉकेट को प्रभावी रूप से कार्यान्वित करने के लिए प्रत्येक सॉकेट को 1 सॉकेटएसिंसेन्ट आर्ट्स से अधिक की आवश्यकता होगी। प्रत्येक SocketAsyncEventArgs में बाइट [] बफर के साथ एक समस्या भी है। संक्षेप में, जब भी प्रबंधित - देशी संक्रमण होता है (भेजना/प्राप्त करना) बाइट बफर को पिन किया जाएगा। यदि आप आवश्यकतानुसार सॉकेटएसिंसेन्ट आर्ट्स और बाइट बफर आवंटित करते हैं तो आप विखंडन के कारण कई ग्राहकों के साथ आउटऑफमेमरी अपवादों में भाग ले सकते हैं और जीसी की निष्क्रियता को संक्रमित करने की अक्षमता के कारण।

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

प्रैक्टिस में मुझे संसाधनों के वितरण को प्रबंधित करने के लिए सॉकेटएसिंसेन्ट आर्ट्स और सॉकेटबफरपूल क्लास के चारों ओर एक रैपर वर्ग बनाने के लिए सबसे अच्छा मिला है।

private void BeginReceive(Socket socket) 
    { 
     Contract.Requires(socket != null, "socket"); 

     SocketEventArgs e = SocketBufferPool.Instance.Alloc(); 
     e.Socket = socket; 
     e.Completed += new EventHandler<SocketEventArgs>(this.HandleIOCompleted); 

     if (!socket.ReceiveAsync(e.AsyncEventArgs)) { 
      this.HandleIOCompleted(null, e); 
     } 
    } 

यहाँ और HandleIOCompleted विधि है::

उदाहरण के लिए, यहाँ एक BeginReceive विधि के लिए कोड है

private void HandleIOCompleted(object sender, SocketEventArgs e) 
    { 
     e.Completed -= this.HandleIOCompleted; 
     bool closed = false; 

     lock (this.sequenceLock) { 
      e.SequenceNumber = this.sequenceNumber++; 
     } 

     switch (e.LastOperation) { 
      case SocketAsyncOperation.Send: 
      case SocketAsyncOperation.SendPackets: 
      case SocketAsyncOperation.SendTo: 
       if (e.SocketError == SocketError.Success) { 
        this.OnDataSent(e); 
       } 
       break; 
      case SocketAsyncOperation.Receive: 
      case SocketAsyncOperation.ReceiveFrom: 
      case SocketAsyncOperation.ReceiveMessageFrom: 
       if ((e.BytesTransferred > 0) && (e.SocketError == SocketError.Success)) { 
        this.BeginReceive(e.Socket); 
        if (this.ReceiveTimeout > 0) { 
         this.SetReceiveTimeout(e.Socket); 
        } 
       } else { 
        closed = true; 
       } 

       if (e.SocketError == SocketError.Success) { 
        this.OnDataReceived(e); 
       } 
       break; 
      case SocketAsyncOperation.Disconnect: 
       closed = true; 
       break; 
      case SocketAsyncOperation.Accept: 
      case SocketAsyncOperation.Connect: 
      case SocketAsyncOperation.None: 
       break; 
     } 

     if (closed) { 
      this.HandleSocketClosed(e.Socket); 
     } 

     SocketBufferPool.Instance.Free(e); 
    } 

ऊपर कोड एक TcpSocket वर्ग कि बढ़ा देंगे में निहित है DataReceived & डेटासेंट घटनाओं। नोटिस करने के लिए एक बात यह है कि सॉकेटएसिंकऑपरेशन। रिसीव मैसेज फ्रॉम: ब्लॉक; अगर सॉकेट में कोई त्रुटि नहीं है तो यह तुरंत एक और BeginReceive() शुरू करता है जो पूल से एक और सॉकेटइवर्ट्स आवंटित करेगा।

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

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

आशा इस मदद करता है, विधेयक

-1

अपने कोड में, आप यह करते हैं:

if (!socket.ReceiveAsync(e.AsyncEventArgs)) { 
    this.HandleIOCompleted(null, e); 
} 

लेकिन यह है कि ऐसा करने के लिए एक त्रुटि है। एक कारण है कि कॉलबैक को सिंक्रनाइज़ करने पर लागू नहीं किया जाता है, इस तरह की कार्रवाई स्टैक को भर सकती है।

कल्पना कीजिए कि प्रत्येक ReceiveAsync हमेशा तुल्यकालिक रूप से लौट रहा है। यदि आपका हैंडल Icomplepleted थोड़ी देर में था, तो आप उसी परिणाम को संसाधित कर सकते हैं जो एक ही स्टैक स्तर पर तुल्यकालिक रूप से लौटा। अगर यह तुल्यकालिक रूप से वापस नहीं आया, तो आप थोड़ी देर तोड़ देते हैं। लेकिन, आप जो कर रहे हैं, आप स्टैक में एक नया आइटम बनाते हैं ... इसलिए यदि आपके पास पर्याप्त भाग्य है, तो आप स्टैक ओवरफ़्लो अपवाद का कारण बनेंगे।

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