2011-12-29 3 views
2

У меня есть пример службы для проверки связи WCF net.tcp. Это очень простой сервис, и все, что он делает, это подписка клиента на услугу, а затем вызов callbackchannel для уведомления всех подключенных клиентов о вещании сообщения. Служба размещена внутри IIS 7.5.WCF duplex Ошибка связи 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)); 
    } 
} 

Я не могу понять поведение я получаю при его запуске. После того, как первый клиент работает, все работает нормально, но когда я закрываю и открываю тестовое клиентское приложение, он выдает исключение, уведомляющее, что канал не может использоваться для связи, так как он находится в состоянии сбоя.

Это образец тестовый клиент:

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); 
     } 
    } 

Ситуация становится даже багги, когда я бегу 5 клиентов. Non тех, кто отправляет или получает сообщения.

ответ

3

Когда клиент вызывает SubcribeToService, вы добавляете его контекст работы в список с именем JoinedClien.

Когда вы вызываете Broadcast на свой сервер, вы вызываете метод NotifyClient во всех собранных контекстах работы для каждого клиента, который когда-либо подключался.

Проблема заключается в том, что отсоединенный клиент не будет удален из вашего списка JoinedClien. При попытке вызвать метод операции в отключенном контексте операции вы получите сообщение об ошибке . Ошибка.

Чтобы работать вокруг, вы должны подписаться на Channel_Closed и Channel_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

Могут ли у вас предоставить дополнительную информацию о блокировке? я имею в виду, что я модифицировал услугу в соответствии с вашими советами, которые я изменил, поэтому у нее есть код проверки для состояния сбоя. –

+0

. Я обновил свой ответ, чтобы показать синхронизированный доступ к вашим общим данным – Jan

+0

и последнему вопросу? можете ли вы дать какое-либо предложение или замечания относительно того, как я реализовал это уведомление и на основе push-сервиса? это правильный способ поддерживать список клиентов и отправлять уведомления? как привязка TCP используется с сеансом? –