2011-12-29 48 views
2

我有一个示例服务来测试WCF net.tcp通信。这是非常简单的服务,它所做的只是向服务订阅客户端,然后调用callbackchannel来通知所有连接的客户端有关广播消息。该服务托管在IIS 7.5中。WCF双工TCP通信错误

这里是服务代码和测试客户端来测试它。

[ServiceContract(CallbackContract = typeof(ISampleServiceCallBack), SessionMode = SessionMode.Required)] 
public interface ISampleCuratioService 
{ 
    [OperationContract(IsOneWay = true)] 
    void SubcribeToService(string sub); 

    [OperationContract] 
    string GetData(int value); 

    [OperationContract(IsOneWay = true)] 
    void Broadcast(string message); 
} 

public interface ISampleServiceCallBack 
{ 
    [OperationContract(IsOneWay = true)] 
    void NotifyClient(string message); 
} 

这里的服务实现:

[ServiceBehavior(Name = "CuratioCSMService", InstanceContextMode = InstanceContextMode.PerSession)] 
public class Service1 : ISampleCuratioService 
{ 
    private static List<ISampleServiceCallBack> JoinedClien = new List<ISampleServiceCallBack>(); 

    public void SubcribeToService(string sub) 
    { 
     var subscriber = OperationContext.Current.GetCallbackChannel<ISampleServiceCallBack>(); 
     if (!JoinedClien.Contains(subscriber)) 
     { 
      JoinedClien.Add(subscriber); 
     } 
    } 

    public string GetData(int value) 
    { 
     return string.Format("You entered: {0}", value); 
    } 

    public void Broadcast(string message) 
    { 
     JoinedClien.ForEach(c => c.NotifyClient("message was received " + message)); 
    } 
} 

我不明白运行时,我得到它的行为。第一个客户端运行后,一切正常,但在关闭并打开测试客户端应用程序时,它会抛出异常,通知该通道不能用于通信,因为它处于故障状态。

这是样品测试客户端:当我运行5个客户

static void Main(string[] args) 
    { 
     var callneckclient = new ServiceClientProxy(); 
     var client = new SampleCuratioServiceClient(new InstanceContext(callneckclient)); 
     client.SubcribeToService("me"); 
     Console.ReadLine(); 

     for (int i = 0; i < 15; i++) 
     { 
      Console.WriteLine(client.GetData(5)); 
      client.Broadcast("this is from client me"); 
     } 
     client.Close(); 
     Console.Read(); 
    } 

    public class ServiceClientProxy : ISampleCuratioServiceCallback, IDisposable 
    { 
     public void NotifyClient(string message) 
     { 
      Console.WriteLine(message); 
     } 

     public void Dispose() 
     { 
      GC.SuppressFinalize(this); 
     } 
    } 

的情况就会更马车。不是那些发送或接收消息。

回答

3

当客户端调用SubcribeToService时,您将其操作上下文添加到名为JoinedClien的列表中。

当您在服务器中调用Broadcast时,您将针对每个连接的客户端在所有收集的操作上下文中调用方法NotifyClient

问题是,断开的客户端不会从您的JoinedClien列表中删除。 当您尝试调用断开的操作上下文的操作方法时,您会收到通道处于错误状态错误。

要解决,你应该订阅Channel_ClosedChannel_Faulted事件,还搭上CommunicationException回拨到您的客户端时,并移除故障客户端的操作语境:

public void Broadcast(string message) 
{ 
    // copy list of clients 
    List<OperationContext> clientsCopy = new List<OperationContext>(); 
    lock(JoinedClien) { 
     clientsCopy.AddRange(JoinedClien); 
    } 

    // send message and collect faulted clients in separate list 
    List<OperationContext> clientsToRemove = new List<OperationContext>(); 
    foreach (var c in JoinedClien) 
    { 
     try { 
      c.NotifyClient("message was received " + message)); 
     } 
     catch (CommunicationException ex) { 
      clientsToRemove.Add(c); 
     } 
    } 

    foreach (var c in clientsToRemove) 
    { 
     lock(JoinedClien) { 
      if(JoinedClien.Contains(c)) 
       JoinedClien.Remove(c); 
     } 
    } 
} 

当增加新客户你也必须锁定该操作:

var subscriber = OperationContext.Current.GetCallbackChannel<ISampleServiceCallBack>(); 
lock(JoinedClien) 
{ 
    if (!JoinedClien.Contains(subscriber)) 
    { 
     JoinedClien.Add(subscriber); 
    } 
} 
+0

是否可以提供有关锁定的额外信息?我的意思是我根据您的建议修改了服务我修改了它,以便检查故障状态的代码 – 2011-12-29 19:58:04

+0

我已更新我的答案以显示您共享数据 – Jan 2011-12-29 20:13:46

+0

和最后一个问题的同步访问权限?你能提供关于我实施这种通知和基于推送服务的方式的任何建议或评论吗?这是维护客户名单和发送通知的正确方法吗?因为TCP绑定与会话一起使用? – 2011-12-29 20:46:56