2011-08-18 27 views
5

मेरे पास क्लाइंट/सर्वर आधारभूत संरचना है। वर्तमान में वे सभी क्लाइंट और सर्वर के बीच डेटा प्राप्त करने के लिए एक टीसीपी क्लाइंट और टीसीपी लिस्टर का उपयोग करते हैं।एकाधिक टीसीपी ग्राहकों को स्वीकार करने का सबसे अच्छा तरीका?

डेटा वर्तमान में प्राप्त होने पर (वर्तमान में अपने धागे पर), यह एक अन्य धागे के लिए एक कतार में रखा जाता है ताकि सॉकेट को मुक्त करने के लिए प्रक्रिया हो सके ताकि यह नया डेटा प्राप्त करने के लिए तैयार और खुला हो।

   // Enter the listening loop. 
       while (true) 
       { 
        Debug.WriteLine("Waiting for a connection... "); 

        // Perform a blocking call to accept requests. 
        using (client = server.AcceptTcpClient()) 
        { 
         data = new List<byte>(); 

         // Get a stream object for reading and writing 
         using (NetworkStream stream = client.GetStream()) 
         { 
          // Loop to receive all the data sent by the client. 
          int length; 

          while ((length = stream.Read(bytes, 0, bytes.Length)) != 0) 
          { 
           var copy = new byte[length]; 
           Array.Copy(bytes, 0, copy, 0, length); 
           data.AddRange(copy); 
          } 
         } 
        } 

        receivedQueue.Add(data); 
       } 

हालांकि मैं यह जानना चाहता था कि ऐसा करने का कोई बेहतर तरीका है या नहीं। उदाहरण के लिए यदि 10 ग्राहक हैं और वे सभी एक ही समय में सर्वर को डेटा भेजना चाहते हैं, तो कोई भी गुजर जाएगा जबकि अन्य सभी असफल हो जाएंगे। या यदि एक ग्राहक के पास धीमा कनेक्शन है और सॉकेट को हॉग करता है तो अन्य सभी संचार बंद हो जाएंगे ।

क्या एक ही समय में सभी ग्राहकों से डेटा प्राप्त करने में सक्षम होने का कोई तरीका नहीं है और डाउनलोड समाप्त होने पर प्रसंस्करण के लिए प्राप्त डेटा को कतार में जोड़ना है?

+0

बेकार प्लग: http://jonathan.dickinsons.co.za/blog/2011/02/net-sockets-and-you/ - यह एसिंक लूप पर संक्षेप में छूता है; और इसमें एक वास्तविक कार्यान्वयन है (आपको 'थ्रेडपूल' का उपयोग नहीं करना चाहिए जैसे कि जमाल सुझाया गया है)। –

उत्तर

15

तो यहां एक उत्तर है जो आपको शुरू कर देगा - जो मेरे blog post से अधिक शुरुआती स्तर है।

नेट में एक एसिंक पैटर्न है जो एक प्रारंभ * और अंत * कॉल के आसपास घूमता है। उदाहरण के लिए - BeginReceive और EndReceive। वे लगभग हमेशा अपने गैर-एसिंक समकक्ष होते हैं (इस मामले में Receive); और सटीक एक ही लक्ष्य प्राप्त करें।

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

कैसे शुरू/अंत पैटर्न

उपयोग करने के लिए हर शुरू * यह गैर async समकक्ष है करने के लिए विधि comparisson में ठीक 2 अधिक बहस करना होगा।पहला एक AsyncCallback है, दूसरा एक वस्तु है। इन दोनों का मतलब क्या है, "जब आप काम करते हैं तो कॉल करने का एक तरीका यहां है" और "यहां कुछ डेटा है जो मुझे उस विधि के अंदर चाहिए।" जिस पद्धति को हमेशा बुलाया जाता है, वही हस्ताक्षर होता है, इस विधि के अंदर आप अंत * समकक्ष को कॉल करते हैं ताकि यह परिणाम प्राप्त हो सके कि आपने यह सिंक्रनाइज़ किया है। उदाहरण के लिए:

private void BeginReceiveBuffer() 
{ 
    _socket.BeginReceive(buffer, 0, buffer.Length, BufferEndReceive, buffer); 
} 

private void EndReceiveBuffer(IAsyncResult state) 
{ 
    var buffer = (byte[])state.AsyncState; // This is the last parameter. 
    var length = _socket.EndReceive(state); // This is the return value of the method call. 
    DataReceived(buffer, 0, length); // Do something with the data. 
} 

यहाँ क्या होता है नेट सॉकेट से डेटा के लिए इंतज़ार कर, जैसे ही यह डेटा यह EndReceiveBuffer कॉल करता है और इसे करने के लिए 'कस्टम डेटा' से होकर गुजरता है (इस मामले buffer में) हो जाता है शुरू होता है है state.AsyncResult के माध्यम से। जब आप EndReceive पर कॉल करते हैं तो यह आपको प्राप्त होने वाले डेटा की लंबाई वापस देगा (या कुछ विफल होने पर अपवाद फेंक देगा)। सॉकेट के लिए

बेहतर पैटर्न

यह फ़ॉर्म आपको केंद्रीय त्रुटि हैंडलिंग दे देंगे - यह कहीं भी इस्तेमाल किया जा सकता है, जहां async पैटर्न एक धारा की तरह 'बात' (जैसे टीसीपी क्रम में आता है यह भेजा गया था लपेटता , इसलिए इसे Stream ऑब्जेक्ट के रूप में देखा जा सकता है)।

private Socket _socket; 
private ArraySegment<byte> _buffer; 
public void StartReceive() 
{ 
    ReceiveAsyncLoop(null); 
} 

// Note that this method is not guaranteed (in fact 
// unlikely) to remain on a single thread across 
// async invocations. 
private void ReceiveAsyncLoop(IAsyncResult result) 
{ 
    try 
    { 
     // This only gets called once - via StartReceive() 
     if (result != null) 
     { 
      int numberOfBytesRead = _socket.EndReceive(result); 
      if(numberOfBytesRead == 0) 
      { 
       OnDisconnected(null); // 'null' being the exception. The client disconnected normally in this case. 
       return; 
      } 

      var newSegment = new ArraySegment<byte>(_buffer.Array, _buffer.Offset, numberOfBytesRead); 
      // This method needs its own error handling. Don't let it throw exceptions unless you 
      // want to disconnect the client. 
      OnDataReceived(newSegment); 
     } 

     // Because of this method call, it's as though we are creating a 'while' loop. 
     // However this is called an async loop, but you can see it the same way. 
     _socket.BeginReceive(_buffer.Array, _buffer.Offset, _buffer.Count, SocketFlags.None, ReceiveAsyncLoop, null); 
    } 
    catch (Exception ex) 
    { 
     // Socket error handling here. 
    } 
} 

स्वीकारना एकाधिक कनेक्शन

क्या आप आम तौर पर कर एक वर्ग है कि आपके सॉकेट आदि (और साथ ही अपने async पाश) शामिल हैं लिख सकते हैं और प्रत्येक ग्राहक के लिए एक बनाने के लिए है। उदाहरण के लिए तो:

public class InboundConnection 
{ 
    private Socket _socket; 
    private ArraySegment<byte> _buffer; 

    public InboundConnection(Socket clientSocket) 
    { 
     _socket = clientSocket; 
     _buffer = new ArraySegment<byte>(new byte[4096], 0, 4096); 
     StartReceive(); // Start the read async loop. 
    } 

    private void StartReceive() ... 
    private void ReceiveAsyncLoop() ... 
    private void OnDataReceived() ... 
} 

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

+0

+1 सभी भयानक सामग्री के लिए धन्यवाद। – Dylan

+0

मैं यह उल्लेख करना भूल गया कि आप ग्राहकों को भी वैसे ही स्वीकार कर सकते हैं, उदा। BeginAcceptTcpClient। आप एसिंक लूप को समान रूप से भी सेट अप कर सकते हैं। –

+1

ब्लॉग पोस्ट लिंक मर चुका है। लेकिन यहां यह archive.org पर है: https://web.archive.org/web/20121127003207/http://jonathan.dickinsons.co.za/blog/2011/02/net-sockets-and-you –

0

मैं आमतौर पर कई थ्रेड के साथ थ्रेड पूल का उपयोग कर रहा हूं। प्रत्येक नए कनेक्शन पर मैं पूल से थ्रेड में से एक में कनेक्शन हैंडलिंग (आपके मामले में - जो भी आप खंड का उपयोग करते हैं) चला रहे हैं।

इससे आप दोनों प्रदर्शन प्राप्त कर सकते हैं क्योंकि आप कई एक साथ स्वीकृत कनेक्शन की अनुमति दे रहे हैं और आप आने वाले कनेक्शनों को संभालने के लिए आवंटित संसाधनों (धागे, आदि) की संख्या को भी सीमित कर रहे हैं।

आप एक अच्छा उदाहरण है here

गुड लक

+0

फिर से थ्रेड-प्रति-क्लाइंट चीज़ के साथ। यह वास्तव में बुरा अभ्यास है। –

1

आप डाटा पढ़ने के अतुल्यकालिक विधि का उपयोग करना चाहिए, एक उदाहरण है:

// Enter the listening loop. 
while (true) 
{ 
    Debug.WriteLine("Waiting for a connection... "); 

    client = server.AcceptTcpClient(); 

    ThreadPool.QueueUserWorkItem(new WaitCallback(HandleTcp), client); 
} 

private void HandleTcp(object tcpClientObject) 
{ 
    TcpClient client = (TcpClient)tcpClientObject; 
    // Perform a blocking call to accept requests. 

    data = new List<byte>(); 

    // Get a stream object for reading and writing 
    using (NetworkStream stream = client.GetStream()) 
    { 
     // Loop to receive all the data sent by the client. 
     int length; 

     while ((length = stream.Read(bytes, 0, bytes.Length)) != 0) 
     { 
      var copy = new byte[length]; 
      Array.Copy(bytes, 0, copy, 0, length); 
      data.AddRange(copy); 
     } 
    } 

    receivedQueue.Add(data); 
} 

इसके अलावा, आप के लिए AutoResetEvent या ManualResetEvent उपयोग करने पर विचार करना चाहिए जब संग्रह में नया डेटा जोड़ा जाता है तो अधिसूचित किया जाए ताकि डेटा को संभालने वाला थ्रेड पता चलेगा कि डेटा कब प्राप्त होता है, और यदि आप 4.0 का उपयोग कर रहे हैं तो आप 0 का उपयोग करने के लिए बेहतर स्विच कर सकते हैं Queue के बजाय।

+0

पहले से ही ब्लॉकिंग कोलेक्शन का उपयोग कर रहा है। और मैं इस तथ्य के कारण सिंक्रोनस विधियों का उपयोग कर रहा हूं कि मेरे पास फाइलें प्राप्त करने के लिए एक समर्पित धागा है। उपरोक्त आपके उदाहरण में, यदि दो क्लाइंट एक ही समय में कनेक्ट होते हैं, तो सर्वर होगा। स्वीकार करें टीसीपी क्लाइंट दोनों स्वीकार करते हैं या टीसीपीलिस्टर अगले उपलब्ध होने तक हैंडल टीसीपी के बाद कतारबद्ध किया जाएगा? एक नोट के रूप में, यदि आप .Net 4 का उपयोग कर रहे हैं तो आपको थ्रेडपूल की बजाय कार्य लाइब्रेरी का उपयोग करना चाहिए। – Dylan

+0

यह दोनों स्वीकार करेगा, यही कारण है कि हम यहां 'थ्रेड' का उपयोग कर रहे हैं, क्योंकि यह किसी अन्य थ्रेड पर पहला डेटा पढ़ेगा, इसलिए यह वर्तमान थ्रेड को अवरुद्ध नहीं करेगा, इसलिए 'ThreadPoo.Queu..' हैंडल को वापस कर देगा कॉलर थ्रेड तुरंत और साथ ही क्लाइंट को संभालने के लिए एक नया धागा बनाते हैं। थ्रेडपूल का उपयोग करने के लिए –

+0

-1। .NET 3.5 में आपको 'प्रारंभ/समाप्ति-प्राप्त' का उपयोग करना चाहिए। नेट 4.0 पर आप नए इवेंटिंग मॉडल या टीपीएल का उपयोग कर सकते हैं। आपका कोड आईओसीपी का बिल्कुल उपयोग नहीं करता है; जो इसे एक गरीब सुझाव देता है। –

1

आपको इसे प्राप्त करने के लिए एसिंक्रोनस सॉकेट प्रोग्रामिंग का उपयोग करना चाहिए। एमएसडीएन द्वारा प्रदत्त example पर एक नज़र डालें।

+1

+1 - सत्य का स्रोत :)। –

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