WCF дуплекс TCP ошибка связи - PullRequest
       21

WCF дуплекс TCP ошибка связи

2 голосов
/ 29 декабря 2011

У меня есть пример сервиса для тестирования связи WCF net.tcp.Это очень простая услуга, и все, что она делает, это подписывает клиента на службу и затем вызывает callbackchannel, чтобы уведомить всех подключенных клиентов о широковещательном сообщении.Служба размещена в IIS 7.5.

Вот код службы и тестовый клиент для его проверки.

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

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

Это пример тестового клиента:

    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 клиентов.Ни один из тех, кто отправляет или получает сообщения.

1 Ответ

3 голосов
/ 29 декабря 2011

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

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

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

Чтобы обойти эту проблему, вы должны подписаться на события 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);
    }
}
...