2011-12-29 76 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