2011-05-25 15 views
6

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

ग्राहक पक्ष पर मैं यह कर रहा हूँ:

NetworkStream ns = tcpClient.GetStream(); 

// Send packet 

byte[] sizePacket = BitConverter.GetBytes(request.Length); 
byte[] requestWithHeader = new byte[sizePacket.Length + request.Length]; 
sizePacket.CopyTo(requestWithHeader, 0); 
request.CopyTo(requestWithHeader, sizePacket.Length); 

ns.Write(requestWithHeader, 0, requestWithHeader.Length); 

// Receive response 

ns.Read(sizePacket, 0, sizePacket.Length); 
int responseLength = BitConverter.ToInt32(sizePacket, 0); 
byte[] response = new byte[responseLength]; 

int bytesReceived = 0; 
while (bytesReceived < responseLength) 
{ 
    int bytesRead = ns.Read(response, bytesReceived, responseLength - bytesReceived); 
    bytesReceived += bytesRead; 
} 

सर्वर विपरीत है (कुछ अपवाद आदि को पकड़ने बाहर छोड़ दिया), यह NetworkStream.Read() जब तक यह एक पूरी है पर ब्लॉक यानी अनुरोध करें, फिर इसे संसाधित करें और लिखें() का उपयोग करके प्रतिक्रिया भेजता है।

लिखने की कच्ची गति()/पढ़ें() एक समस्या नहीं है (यानी बड़े पैकेट भेजना तेज है), लेकिन कई छोटे पैकेट भेजना, एक के बाद एक कनेक्शन को बंद किए बिना, बहुत धीमा हो सकता है 50-100 एमएस)। अजीब बात यह है कि ये देरी सामान्य पिंग टाइम्स < 1 एमएस के साथ लैन कनेक्शन पर दिखाई देती हैं, लेकिन यदि सर्वर स्थानीयहोस्ट पर चल रहा है, तो वे तब नहीं होते हैं, भले ही पिंग समय प्रभावी ढंग से समान हो (कम से कम अंतर नहीं होना चाहिए 100 एमएस के आदेश पर)। यह मुझे समझ में आएगा कि अगर मैं हर पैकेट पर कनेक्शन दोबारा खोल रहा था, जिससे बहुत सारे हैंडशेकिंग हुए, लेकिन मैं नहीं हूं। ऐसा लगता है जैसे सर्वर एक प्रतीक्षा स्थिति में जा रहा है, इसे क्लाइंट के साथ सिंक से बाहर फेंकता है, और फिर यह थोड़ा सा ठोकर खा जाता है क्योंकि यह अनिवार्य रूप से खोया कनेक्शन क्या है।

तो, क्या मैं इसे गलत कर रहा हूं? क्या TcpServer और TcpClient सिंक्रनाइज़ किए गए कनेक्शन को रखने का कोई तरीका है ताकि सर्वर हमेशा डेटा प्राप्त करने के लिए तैयार हो? (और इसके विपरीत: कभी-कभी क्लाइंट से अनुरोध को संसाधित करने में कुछ एमएस लगते हैं, और तब क्लाइंट सर्वर से प्रतिक्रिया प्राप्त करने के लिए तैयार नहीं लगता है जब तक कि इसे पढ़ने पर अवरुद्ध करने के बाद जागने के कुछ क्षण नहीं होते हैं() ।)

+0

क्या आपने स्ट्रीमरिएडर का उपयोग करने की जिज्ञासा से बाहर किया? –

+1

कोई विशेष कारण नहीं है कि आप [डब्ल्यूसीएफ] (http://msdn.microsoft.com/en-us/netframework/aa663324) का उपयोग क्यों नहीं कर रहे हैं? इस ढांचे में सब कुछ करने की कोशिश कर रहा है और अधिक निर्मित है। – Jay

+2

इसके विपरीत आईएमओ; डब्ल्यूसीएफ बेहद फूला हुआ है, और ज्यादातर लोग केवल इसका एक छोटा सा अंश उपयोग करते हैं। कच्चे सॉकेट अक्सर ठीक होते हैं, और आमतौर पर काफी अधिक कुशल होते हैं। –

उत्तर

7

यह पता चला है कि मेरा सर्वर और क्लाइंट पूरी तरह से सममित नहीं था। मैंने देखा था, लेकिन मुझे नहीं लगता था कि यह बिल्कुल महत्वपूर्ण है। जाहिर है यह एक बड़ा सौदा है।

ns.Write(sizePacket, 0, sizePacket.Length); 
ns.Write(response, 0, response.Length); 

कौन सा मैं इस में बदल: विशेष रूप से, सर्वर ऐसा किया

// ... concatenate sizePacket and response into one array, same as client code above 
ns.Write(responseWithHeader, 0, responseWithHeader.Length); 

और अब देरी पूरी तरह से चला गया है, या कम से कम यह अब मिलीसेकेंड में औसत दर्जे का है। तो यह ठीक है 100x गति की तरह कुछ है। \ o/

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

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

+9

बस देरी की व्याख्या करने के लिए, मेरा मानना ​​है कि आपने विंडोज सॉकेट में नागल्स एल्गोरिदम को मारा: नेटवर्क समग्र प्रदर्शन को बेहतर बनाने के लिए छोटे पैकेट को बफर में रखा जाएगा। नेटवर्क पीयर तक पहुंचने से पहले प्रत्येक एकल पैकेट 200ms विलंब देख सकता है: http://support.microsoft.com/kb/214397/en-us http://msdn.microsoft.com/en-us/library/system.net .sockets.tcpclient.nodelay.aspx –

+1

ठीक है, इसके लिए धन्यवाद, यह काफी जानकारीपूर्ण था। मेरी इच्छा है कि माइक्रोसॉफ्ट एमएसडीएन से उत्कृष्ट लेखों को जोड़ने में बेहतर था। या शायद मुझे यह जानने की जरूरत है कि कहां देखना है। किसी भी तरह से, अब मैं समझदार महसूस करता हूं। अभी भी थोड़ा विचित्र है कि मैं ~ 100 एमएस देरी को माप रहा था, विंसॉक बफर के लिए 200 एमएस टाइमआउट की तरह कुछ नहीं, लेकिन शायद मैं इसे गलत माप रहा था। – ReturningTarzan

+4

यह माइक्रोसॉफ्ट के लिए वास्तव में विशिष्ट नहीं है (एल्गोरिदम की अपनी पसंद के अलावा)। कोई भी उत्पादन टीसीपी स्टैक ओवरहेड को कम करने के लिए छोटे पैकेट पर बड़े पैकेट भेजने का पक्ष लेगा। इसलिए जब आप टीसीपी धारा में थोड़ी सी मात्रा लिखते हैं तो उम्मीदों में यह बफर किया जाएगा कि कुछ अन्य लेखन होंगे। एक अच्छा टीसीपी स्टैक एल्गोरिदम कम विलंबता (तुरंत भेजकर) और उच्च बैंडविड्थ (संभवतः सबसे बड़ा पैकेट आकार का उपयोग करके) को संतुलित करने का प्रयास करता है। अधिकांश स्टैक्स मैन्युअल रूप से कमांड पर बफर को फ्लश करने का तरीका प्रदान करते हैं, या बेहतर प्रदर्शन के लिए किसी विशिष्ट सॉकेट पर कोई देरी निर्दिष्ट नहीं करते हैं। – David

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