He seguido la demostración de Tomek Janczuk en silverlight tv para crear un programa de chat que utiliza el servicio web WCF Duplex Polling. El cliente se suscribe al servidor y luego el servidor inicia las notificaciones a todos los clientes conectados para publicar eventos.interbloqueo al utilizar WCF Duplex Polling con Silverlight
La idea es simple, en el cliente, hay un botón que permite que el cliente se conecte. Un cuadro de texto donde el cliente puede escribir un mensaje y publicarlo, y un cuadro de texto más grande que presenta todas las notificaciones recibidas del servidor.
He conectado 3 clientes (en diferentes navegadores - IE, Firefox y Chrome) y todo funciona muy bien. Envían mensajes y los reciben sin problemas. El problema comienza cuando cierro uno de los navegadores. Tan pronto como un cliente sale, los otros clientes se quedan atascados. Dejan de recibir notificaciones.
Supongo que el bucle en el servidor que pasa por todos los clientes y les envía las notificaciones está atascado en el cliente que ahora falta. Intenté capturar la excepción y eliminarla de la lista de clientes (ver código) pero todavía no ayuda.
¿Alguna idea?
El código del servidor es la siguiente:
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);
}
}
}
}
El código de cliente es el siguiente:
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";
}
}
Y el web.config es el siguiente:
<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>
Me trataron de solucionar este problema haciendo que las múltiples notificaciones Enhebrado Entonces, en lugar de notificar a los clientes en el ciclo, creo un hilo por cliente y dejo que los hilos notifiquen a los clientes, por lo que un cliente no tiene que esperar al otro. Parece resolver ese problema, pero todavía hay un problema extraño. Cuando el cliente se está ejecutando en IE, si no hay actividad durante 10 segundos, la conexión está muerta. Y la próxima vez que intente enviar un mensaje recibe una excepción que indica que la conexión está en estado de error. Esto solo sucede en IE ... ¿alguien tiene alguna idea? –