2011-01-10 17 views
5

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

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

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

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

कोई विचार?

void client_NotifyReceived(object sender, ChatServiceProxy.NotifyReceivedEventArgs e) 
{ 
    this.Messages.Text += string.Format("{0}\n\n", e.Error != null ? e.Error.ToString() : e.message); 
} 

private void MyMessage_KeyDown(object sender, KeyEventArgs e) 
{ 
    if (e.Key == Key.Enter) 
    { 
     this.client.PublishAsync(this.MyMessage.Text); 
     this.MyMessage.Text = ""; 
    } 
} 

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    this.client = new ChatServiceProxy.ChatServiceClient(new PollingDuplexHttpBinding { DuplexMode = PollingDuplexMode.MultipleMessagesPerPoll }, new EndpointAddress("../ChatService.svc")); 

    // listen for server events 
    this.client.NotifyReceived += new EventHandler<ChatServiceProxy.NotifyReceivedEventArgs>(client_NotifyReceived); 

    this.client.SubscribedReceived += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(client_SubscribedReceived); 

    // subscribe for the server events 
    this.client.SubscribeAsync(); 

} 

void client_SubscribedReceived(object sender, System.ComponentModel.AsyncCompletedEventArgs e) 
{ 
    try 
    { 
     Messages.Text += "Connected!\n\n"; 
     gsConnect.Color = Colors.Green; 
    } 
    catch 
    { 
     Messages.Text += "Failed to Connect!\n\n"; 

    } 
} 

और वेब config इस प्रकार है:: स्थापित करने के लिए

<system.serviceModel> 
    <extensions> 
     <bindingExtensions> 
     <add name="pollingDuplex" type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement, System.ServiceModel.PollingDuplex, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
     </bindingExtensions> 
    </extensions> 
    <behaviors> 
     <serviceBehaviors> 
     <behavior name=""> 
      <serviceMetadata httpGetEnabled="true"/> 
      <serviceDebug includeExceptionDetailInFaults="false"/> 
     </behavior> 
     </serviceBehaviors> 
    </behaviors> 
    <bindings> 
     <pollingDuplex>   
     <binding name="myPollingDuplex" duplexMode="MultipleMessagesPerPoll"/> 
     </pollingDuplex> 
    </bindings> 
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/> 
    <services> 
     <service name="ChatDemo.Web.ChatService"> 
     <endpoint address="" binding="pollingDuplex" bindingConfiguration="myPollingDuplex" contract="ChatDemo.Web.ChatService"/> 
     <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> 
     </service> 
    </services> 
    </system.serviceModel> 
+0

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

उत्तर

2

ठीक है, मैं अंत में एक समाधान मिल गया। इसकी तरह एक गंदे पैच है, लेकिन यह काम करता है और इसकी स्थिरता है, इसलिए मैं इसका उपयोग करूंगा।

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

अंततः मैं 2 समस्याओं को अलग करने में कामयाब रहा और फिर प्रत्येक समस्या को अपना समाधान दिया।

समस्या संख्या 1: डिस्कनेक्ट किए गए क्लाइंट को अधिसूचना भेजने की कोशिश करते समय सर्वर लंबे समय तक लटकता है।

इस के बाद से एक पाश में किया गया था, अन्य ग्राहकों के साथ इंतजार करना पड़ा: मैं पाश प्रत्येक ग्राहक के लिए एक अलग थ्रेड प्रारंभ कर दिया, इस समस्या को हल करने के लिए तो

foreach (IChatNotification channel in this.clients) 
      { 
       try 
       { 
        channel.Notify(message); // if this channel is dead, the next iteration will be delayed 
       } 
       catch 
       { 
        toRemove.Add(channel); 
       } 
      } 

, है, तो सूचनाएं ग्राहकों के लिए स्वतंत्र हो जाते हैं।यहां अंतिम कोड है:

[OperationContract(IsOneWay = true)] 
public void Publish(string message) 
{ 
    lock (this.clients) 
    { 
     foreach (IChatNotification channel in this.clients) 
     { 
      Thread t = new Thread(new ParameterizedThreadStart(this.notifyClient)); 
      t.Start(new Notification{ Client = channel, Message = message }); 
     } 
    } 

} 

public void notifyClient(Object n) 
{ 
    Notification notif = (Notification)n; 
    try 
    { 
     notif.Client.Notify(notif.Message); 
    } 
    catch 
    { 
     lock (this.clients) 
     { 
      this.clients.Remove(notif.Client); 
     } 
    } 
} 

ध्यान दें कि प्रत्येक ग्राहक अधिसूचना को संभालने के लिए एक धागा है। अगर यह अधिसूचना भेजने में असफल रहा तो थ्रेड क्लाइंट को भी त्याग देता है।

समस्या संख्या 2: ग्राहक 10 निष्क्रिय सेकंड के बाद कनेक्शन को मारता है।

यह समस्या आश्चर्यजनक रूप से, केवल खोजकर्ता में हुई ... मैं वास्तव में इसे समझा नहीं सकता, लेकिन Google में कुछ शोध करने के बाद मैंने पाया कि मैं इसे नोटिस करने वाला अकेला नहीं था, लेकिन कोई भी नहीं मिला स्पष्ट समाधान को छोड़कर साफ समाधान - "बस हर 9 सेकंड में सर्वर को पिंग करें"। जो मैंने किया वह ठीक है।

तो मैं अनुबंध इंटरफ़ेस बढ़ाया एक सर्वर पिंग विधि है, जो तुरंत एक ग्राहक की पोंग प्रणाली को बुलाती है शामिल करने के लिए:

[OperationContract(IsOneWay = true)] 
public void Ping() 
{ 
    IChatNotification cli = OperationContext.Current.GetCallbackChannel<IChatNotification>(); 
    cli.Pong(); 
} 

ग्राहक की पोंग ईवेंट हैंडलर एक धागा है कि 9 सेकंड के लिए सोता है बनाता है और फिर पिंग कॉल विधि फिर से:

void client_PongReceived(object sender, System.ComponentModel.AsyncCompletedEventArgs e) 
{ 
    // create a thread that will send ping in 9 seconds 
    Thread t = new Thread(new ThreadStart(this.sendPing)); 
    t.Start(); 
} 

void sendPing() 
{ 
    Thread.Sleep(9000); 
    this.client.PingAsync(); 
} 

और वह था। मैंने इसे कई क्लाइंट्स के साथ परीक्षण किया, कुछ ब्राउज़रों को अपने ब्राउज़र बंद करके हटा दिया, फिर उन्हें लॉन्च किया, यह सब काम किया। और खोए गए ग्राहकों को अंततः सर्वर द्वारा साफ़ किया गया था।

एक और नोट - के बाद से ग्राहक कनेक्शन अविश्वसनीय साबित हुई, मैं इसे एक कोशिश के साथ घिरा हुआ - पकड़ अपवाद जहां कनेक्शन अनायास मर जाता है तो मैं मामलों का जवाब कर सकते हैं:

 try 
     { 
      this.client.PublishAsync(this.MyMessage.Text); 
      this.MyMessage.Text = ""; 
     } 
     catch 
     { 
      this.Messages.Text += "Was disconnected!"; 
      this.client = null; 
     } 

यह, ज़ाहिर है, मदद नहीं करता है, क्योंकि "PublishAsync" तत्काल, और सफलतापूर्वक लौटाता है, जबकि कोड स्वचालित रूप से जेनरेट किया गया था (Reference.cs में) सर्वर को संदेश को किसी अन्य थ्रेड में भेजने का वास्तविक कार्य करता है। इस अपवाद को पकड़ने का एकमात्र तरीका स्वचालित रूप से जेनरेट की गई प्रॉक्सी को अपडेट करना है ... जो एक बहुत बुरा विचार है ... लेकिन मुझे कोई अन्य तरीका नहीं मिला। (विचारों की सराहना की जाएगी)।

यह सब कुछ है। अगर किसी को इस मुद्दे के आसपास काम करने का आसान तरीका पता है, तो मुझे सुनने में खुशी होगी।

[OperationContract(IsOneWay = true, AsyncPattern = true)] 
    IAsyncResult BeginNotification(string message, AsyncCallback callback, object state); 
    void EndNotification(IAsyncResult result); 

जब सर्वर शेष ग्राहकों यह मुद्दों को सूचित करता है:

चीयर्स,

कोबी

+0

जब आप वापस ग्राहक को कॉल कर रहे हैं, धागा निर्माण संभाल खुद के बजाय, आप पर भरोसा करने के लिए कर सकते हैं थ्रेडपूल यह आसान कोड है, और आपको बहुत सारे धागे के साथ सिस्टम को गलती से अधिभारित करने की चिंता करने की आवश्यकता नहीं है। आप Async कॉलबैक मॉडल का भी उपयोग कर सकते हैं, लेकिन कोड काफी जटिल है: वास्तव में अनुशंसित नहीं है। निष्क्रियता के 10 सेकंड के बाद आईई कनेक्शन के संबंध में, क्या आपने सिल्वरलाइट को यह बताने के लिए WebRequest.RegisterPrefix() का उपयोग करने का प्रयास किया है कि आप ब्राउज़र के बजाय अपने आंतरिक HTTP स्टैक का उपयोग करना चाहते हैं? –

+1

हाय केन, आपकी टिप्पणी के लिए thnx। क्या आप कृपया इन दो विषयों पर विस्तृत जानकारी दे सकते हैं: थ्रेडपूल और आंतरिक HTTP स्टैक ब्राउज़र के साथ? मुझे इसके बारे में और अधिक सुनना अच्छा लगेगा। –

2

कोशिश के रूप में निम्नानुसार

using System; 
using System.Linq; 
using System.Runtime.Serialization; 
using System.ServiceModel; 
using System.ServiceModel.Activation; 
using System.Collections.Generic; 
using System.Runtime.Remoting.Channels; 

namespace ChatDemo.Web 
{ 
    [ServiceContract] 
    public interface IChatNotification 
    { 
     // this will be used as a callback method, therefore it must be one way 
     [OperationContract(IsOneWay=true)] 
     void Notify(string message); 

     [OperationContract(IsOneWay = true)] 
     void Subscribed(); 
    } 

    // define this as a callback contract - to allow push 
    [ServiceContract(Namespace="", CallbackContract=typeof(IChatNotification))] 
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] 
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] 
    public class ChatService 
    { 
     SynchronizedCollection<IChatNotification> clients = new SynchronizedCollection<IChatNotification>(); 

     [OperationContract(IsOneWay=true)] 
     public void Subscribe() 
     { 
      IChatNotification cli = OperationContext.Current.GetCallbackChannel<IChatNotification>(); 
      this.clients.Add(cli); 
      // inform the client it is now subscribed 
      cli.Subscribed(); 

      Publish("New Client Connected: " + cli.GetHashCode()); 

     } 

     [OperationContract(IsOneWay = true)] 
     public void Publish(string message) 
     { 
      SynchronizedCollection<IChatNotification> toRemove = new SynchronizedCollection<IChatNotification>(); 

      foreach (IChatNotification channel in this.clients) 
      { 
       try 
       { 
        channel.Notify(message); 
       } 
       catch 
       { 
        toRemove.Add(channel); 
       } 
      } 

      // now remove all the dead channels 
      foreach (IChatNotification chnl in toRemove) 
      { 
       this.clients.Remove(chnl); 
      } 
     } 
    } 
} 

ग्राहक कोड है:

सर्वर कोड इस प्रकार है निष्क्रियता मध्यांतर। पहले एक ही समस्या थी। मेरे लिए काम किया। pollingDuplex inactivityTimeout = "02:00:00" serverPollTimeout = "00:05:00" maxPendingMessagesPerSession = "2147483647" maxPendingSessions = "2147483647" duplexMode = "SingleMessagePerPoll"

+1

Thnx, लेकिन मुझे काफी समझ में नहीं आता है। मैं इन मानों को कहां सेट करूं? ग्राहक या सर्वर पर? –

+0

यह है कि मैं क्या है: <बाध्यकारी नाम = "PollingDuplexBindingServer"> msqsf

1

# 1 समस्या को हल करने का एक बेहतर तरीका async पद्धति का उपयोग कर कॉलबैक स्थापित करने के लिए है पहली छमाही:

channel.BeginNotification(message, NotificationCompletedAsyncCallback, channel); 

इस तरह शेष ग्राहकों को नोटिफ़ी मिलती है एड को छोड़कर क्लाइंट पर टाइम-आउट की प्रतीक्षा किए बिना एड।

अब

private static void NotificationCompleted(IAsyncResult result) 

के रूप में स्थिर पूरा पद्धति स्थापित यह पूरा हो विधि में इस तरह कॉल की शेष आधा फोन:

IChatNotification channel = (IChatNotification)(result.AsyncState); 
    channel.EndNotification(result);