Silverlight TVでTomek Janczukのデモンストレーションを行い、WCF Duplex Polling Webサービスを使用するチャットプログラムを作成しました。クライアントはサーバーにサブスクライブし、サーバーは接続されたすべてのクライアントにイベントをパブリッシュするための通知を開始します。SilverlightでWCFデュプレックスポーリングを使用するとデッドロックが発生する
アイデアは簡単ですが、クライアントにはクライアントが接続できるボタンがあります。クライアントがメッセージを書き込んで発行できるテキストボックス、およびサーバーから受信したすべての通知を表示する大きなテキストボックス。
私は3つのクライアント(IE、Firefox、Chromeの異なるブラウザ)を接続していて、すべてうまく動作します。メッセージを送信してスムーズに受信します。この問題は、ブラウザの1つを閉じると始まります。 1人のクライアントが出るとすぐに、他のクライアントは立ち往生します。彼らは通知を受けるのをやめます。
私は、すべてのクライアントを経由して通知を送信するサーバーのループが、現在失われているクライアントで停止していると推測しています。私は例外をキャッチして、クライアントリスト(コードを参照)から削除しようとしましたが、それでも助けにはなりません。
次のように
サーバー・コードは次のとおりです。
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";
}
}
、次のようにWeb構成は次のとおりです:
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);
}
}
}
}
次のようにクライアントコードがある
<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>
:このような呼び出しの残りの半分を呼び出す今、この完成方法で
ような方法を完了し、静的に設定
スレッド化されています。そのため、ループ内のクライアントに通知するのではなく、クライアントごとにスレッドを作成し、スレッドにクライアントに通知させるので、一方のクライアントはもう一方のクライアントを待機する必要はありません。それはその問題を解決するようだが、まだ奇妙な問題がある。クライアントがIEで動作しているときに、10秒間アクティビティがない場合、接続は切断されています。次にメッセージを送信しようとすると、接続がフォールト状態であることを示す例外が発生します。これはIEでのみ発生します...誰も何か考えている? –