2012-03-14 17 views
9

स्थिति का एक संक्षिप्त सारांश:क्या यह पता लगाना संभव है कि क्लाइंट द्वारा स्ट्रीम बंद कर दिया गया है या नहीं?

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

समाधान मैं वर्तमान में लागू करने के लिए कोशिश कर रहा हूँ:

अनिवार्य रूप से, मैं जोड़ी कोशिश कर रहा हूँ एक NetworkStream के साथ एक SslStream (आवक) (निवर्तमान - एक सॉकेट हो सकता है, लेकिन मैं इसे एक में डाल दिया इनकमिंग से मेल खाने के लिए नेटवर्कस्ट्रीम) और दोनों के लिए पढ़ने/लिखने के संचालन हैं। यह लिंक क्लाइंट (एसएसएल/टीएलएस पर) और सेवा (एक असुरक्षित कनेक्शन पर) के बीच प्रवाह प्रदान करेगा।

यहाँ वर्ग मैं के साथ आया था इन स्ट्रीम से जोड़ने के लिए है:

public class StreamConnector 
{ 
    public StreamConnector(Stream s1, Stream s2) 
    { 
     StreamConnectorState state1 = new StreamConnectorState(s1, s2); 
     StreamConnectorState state2 = new StreamConnectorState(s2, s1); 
     s1.BeginRead(state1.Buffer, 0, state1.Buffer.Length, new AsyncCallback(ReadCallback), state1); 
     s2.BeginRead(state2.Buffer, 0, state2.Buffer.Length, new AsyncCallback(ReadCallback), state2); 
    } 

    private void ReadCallback(IAsyncResult result) 
    { 
     // Get state object. 
     StreamConnectorState state = (StreamConnectorState)result.AsyncState; 

     // Finish reading data. 
     int length = state.InStream.EndRead(result); 

     // Write data. 
     state.OutStream.Write(state.Buffer, 0, length); 

     // Wait for new data. 
     state.InStream.BeginRead(state.Buffer, 0, state.Buffer.Length, new AsyncCallback(ReadCallback), state); 
    } 
} 

public class StreamConnectorState 
{ 
    private const int BYTE_ARRAY_SIZE = 4096; 

    public byte[] Buffer { get; set; } 
    public Stream InStream { get; set; } 
    public Stream OutStream { get; set; } 

    public StreamConnectorState(Stream inStream, Stream outStream) 
    { 
     Buffer = new byte[BYTE_ARRAY_SIZE]; 
     InStream = inStream; 
     OutStream = outStream; 
    } 
} 

समस्या:

जब ग्राहक जानकारी और SslStream के disposes भेजने किया जाता है, सर्वर नहीं इस तरह का संकेत है कि यह हुआ है या नहीं। यह StreamConnector क्लास खुशी से किसी भी तरह की त्रुटि फेंकने के बिना अनंत काल में दौड़ता रहता है, और मुझे कोई संकेतक नहीं मिल रहा है कि इसे रोकना चाहिए। (निश्चित रूप से, तथ्य यह है कि मुझे रीडकॉलबैक में हर बार 0 लंबाई मिलती है, लेकिन मुझे लंबे समय से चलने वाले कनेक्शन प्रदान करने में सक्षम होना चाहिए, इसलिए यह न्याय करने का एक अच्छा तरीका नहीं है।)

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

मेरे सवालों का:

1) वहाँ एक रास्ता है, तो एक स्ट्रीम ग्राहक की ओर से बंद कर दिया गया बताने के लिए है?

2) क्या ऐसा करने का एक बेहतर तरीका है जिसे मैं करने की कोशिश कर रहा हूं?

2 ए) क्या एसिंक्रोनस रीड/राइट लूप चलाने के लिए एक और अधिक प्रभावी तरीका है?

संपादित करें: धन्यवाद, रॉबर्ट। लूप को बुलाया जाता है क्योंकि मैं धाराओं को बंद नहीं कर रहा था (यह जानने के कारण कि धाराओं को बंद करने की आवश्यकता कब है)। मैं इस मुद्दे में किसी और मामले में किसी को चलाता है पर पूरा कोड समाधान शामिल कर रहा हूँ:

/// <summary> 
/// Connects the read/write operations of two provided streams 
/// so long as both of the streams remain open. 
/// Disposes of both streams when either of them disconnect. 
/// </summary> 
public class StreamConnector 
{ 
    public StreamConnector(Stream s1, Stream s2) 
    { 
     StreamConnectorState state1 = new StreamConnectorState(s1, s2); 
     StreamConnectorState state2 = new StreamConnectorState(s2, s1); 
     s1.BeginRead(state1.Buffer, 0, state1.Buffer.Length, new AsyncCallback(ReadCallback), state1); 
     s2.BeginRead(state2.Buffer, 0, state2.Buffer.Length, new AsyncCallback(ReadCallback), state2); 
    } 

    private void ReadCallback(IAsyncResult result) 
    { 
     // Get state object. 
     StreamConnectorState state = (StreamConnectorState)result.AsyncState; 

     // Check to make sure Streams are still connected before processing. 
     if (state.InStream.IsConnected() && state.OutStream.IsConnected()) 
     { 
      // Finish reading data. 
      int length = state.InStream.EndRead(result); 

      // Write data. 
      state.OutStream.Write(state.Buffer, 0, length); 

      // Wait for new data. 
      state.InStream.BeginRead(state.Buffer, 0, state.Buffer.Length, new AsyncCallback(ReadCallback), state); 
     } 
     else 
     { 
      // Dispose of both streams if either of them is no longer connected. 
      state.InStream.Dispose(); 
      state.OutStream.Dispose(); 
     } 
    } 
} 

public class StreamConnectorState 
{ 
    private const int BYTE_ARRAY_SIZE = 4096; 

    public byte[] Buffer { get; set; } 
    public Stream InStream { get; set; } 
    public Stream OutStream { get; set; } 

    public StreamConnectorState(Stream inStream, Stream outStream) 
    { 
     Buffer = new byte[BYTE_ARRAY_SIZE]; 
     InStream = inStream; 
     OutStream = outStream; 
    } 
} 

public static class StreamExtensions 
{ 
    private static readonly byte[] POLLING_BYTE_ARRAY = new byte[0]; 

    public static bool IsConnected(this Stream stream) 
    { 
     try 
     { 
      // Twice because the first time will return without issue but 
      // cause the Stream to become closed (if the Stream is actually 
      // closed.) 
      stream.Write(POLLING_BYTE_ARRAY, 0, POLLING_BYTE_ARRAY.Length); 
      stream.Write(POLLING_BYTE_ARRAY, 0, POLLING_BYTE_ARRAY.Length); 
      return true; 
     } 
     catch (ObjectDisposedException) 
     { 
      // Since we're disposing of both Streams at the same time, one 
      // of the streams will be checked after it is disposed. 
      return false; 
     } 
     catch (IOException) 
     { 
      // This will be thrown on the second stream.Write when the Stream 
      // is closed on the client side. 
      return false; 
     } 
    } 
} 
+0

कृपया 'सी #' के साथ शीर्षक उपसर्ग न करें, यही टैग है :) – kprobst

+0

क्षमा करें। :) जब मैं शोध कर रहा था, तो कुछ अन्य खिताबों में भाषा का जिक्र देखा; सोचा कि यह सहायक होगा। यहां से टैग से चिपके रहेंगे। – zimdanen

उत्तर

3

आप पढ़ सकते हैं या एक सॉकेट में लिखने के लिए प्रयास करने के लिए है - एक डिस्कनेक्ट पता लगाने के लिए - यह के आधार पर या कुछ भी।

लिखने का प्रयास एक अपवाद फेंक देगा/एक त्रुटि लौटाएगा (आपकी भाषा के प्रतिमान के आधार पर) या संभवतः केवल 0 बाइट लिखें। पढ़ने का प्रयास करने से या तो अपवाद फेंक दिया जाएगा/एक त्रुटि वापस आ जाएगी (फिर से आपकी भाषा के प्रतिमान के आधार पर) या null लौटाएं।

यह है कि आप एक का चयन आधारित सर्वर मॉडल का उपयोग कर रहे हैं, कट सॉकेट दिखाता है ध्यान देने योग्य है - यानी रिटर्न चयन - के रूप में पठनीय जब यह डिस्कनेक्ट है, तो आप इसे से पढ़ सकते हैं और त्रुटि मिलती है या करने का प्रयास null

+0

तो मुझे प्रत्येक स्ट्रीम को लिखने का प्रयास करना होगा (जिसके परिणामस्वरूप स्ट्रीम पर खराब डेटा हो सकता है अगर यह बंद नहीं है)? – zimdanen

+0

मान लीजिए, अगर मैं 0 बाइट्स लिख रहा हूं, तो यह कोई मुद्दा नहीं होना चाहिए। – zimdanen

+2

क्या मैं इसे [** निटो **] के अनुसार जोड़ सकता हूं (http://nitoprograms.blogspot.co.il/2009/05/detection-of-half-open-dropped.html) गिराए जाने का एकमात्र तरीका कनेक्शन स्ट्रीम को लिखना है। –

0

मैं मदद नहीं कर सकता लेकिन सोचता हूं कि किसी क्लाइंट को किसी प्रकार के संदेश के साथ सर्वर को बताना चाहिए। एक तार काटने या बिजली में असफल होने या प्लग खींचने के लिए तैयार होना हमेशा सर्वोत्तम होता है, लेकिन आम तौर पर आप किसी प्रकार के एंड-ऑफ-मैसेज मार्कर के साथ कनेक्शन समाप्त करना चाहते हैं। सामान्य समस्याओं के लिए अपवादों को वास्तविक समस्याओं के लिए सहेजें।

+0

क्लाइंट पर एसएसएलस्ट्रीम का निपटारा करते समय (जो नेटवर्कस्ट्रीम का मालिक है, जो सॉकेट का मालिक है), सर्वर को कुछ भी नहीं भेजा जाता है ताकि यह पता चल सके कि यह डिस्कनेक्ट हो गया है। कम से कम, सर्वर पर सॉकेट/स्ट्रीम को बंद करने वाला कुछ भी नहीं। – zimdanen

+0

@zimdanen: मुझे लगता है कि एसएसएलस्ट्रीम के साथ एक (बड़ी) डिजाइन समस्या के रूप में। यदि एसएसएलस्ट्रीम आपके अधिकार से परे है तो आपने जो किया है उसके अलावा बहुत कुछ नहीं किया जाना चाहिए। हमें अपने सॉफ़्टवेयर को दुनिया के साथ काम करना है क्योंकि यह होना चाहिए। (हालांकि, मैं इसके बारे में ग्रंपिंग का विरोध नहीं कर सकता।) – RalphChapin

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