WCF双工频道:检查回叫频道是否仍然可用

时间:2009-12-30 16:38:10

标签: wcf client duplex-channel

我有以下问题。我正在写聊天软件。客户端/服务器机制基于WCF的DualHttpBinding。这意味着,如果用户发送消息,则服务器会通知发送消息的房间中的所有客户端。

我想确保,如果客户端的应用程序崩溃(无论如何),客户端对象将从房间列表中删除。

在调用回调操作之前是否有可能检查回调通道的状态?问题是,如果我在不再连接的客户端上调用操作(由于意外崩溃),该服务将挂起。

 public YagzResult SendMessage(Message message)
    {
        foreach (ChatNodeAddress chatNodeAddress in message.Destination)
        {
            ChatNode chatNode = chatProvider.FindChatNode(chatNodeAddress);
            if (chatNode != null)
            {
                User currentUser = CurrentUser;
                foreach (User user in chatNode)
                {
                    //Don't notify the current client. Deadlock!
                    if (!user.Equals(currentUser))
                    {
                        //Get the callback channel here
                        IYagzClient client = GetClientByUser(user);

                        if (client != null)
                        {
                            //--> If the client here called is not any more available,
                            //the service will hang <---
                            client.OnChatMessageReceived(message);
                        }
                    }
                }
            }
            else
            {
                return YagzResult.ChatNodeNotFound;
            }
        }
        return YagzResult.Ok;
    }

如何检查客户端是否仍在收听?顺便说一下,客户端调用的操作都声明为OneWay,ConcurrencyMode设置为“Multiple”。

谢谢大家!

迎接,

西蒙

3 个答案:

答案 0 :(得分:8)

您可以将回调合约转换为ICommunicationObject,然后检查频道状态。

答案 1 :(得分:1)

对于Closed和Faulted,CommunicationObject(即回调通道)上有事件。您可能希望为这些添加处理程序并跟踪哪些客户端仍有可用的有效通道。

您还可以查看IChannelInitializer类以实现客户端跟踪。

答案 2 :(得分:1)

主要问题是我没有得到任何例外,除了TimeoutException。我的服务被阻止1分钟(我设置的超时),直到异常被触发。

我通过以下解决方法解决了这个问题。我没有在服务的当前工作线程上调用客户端回调操作,而是创建了一个调用客户端回调操作并等待TimeoutException的新线程。如果发生超时,则只需将用户从他所属的聊天室列表中删除。

这是一段代码片段,展示了我是如何做到的:

首先,我创建了一个表示对客户端的单个调用的类:

class YagzClientAsyncCall<T>
{
    /// <summary> Gets or sets the parameter of the client callback. </summary>
    /// <value> The parameter. </value>
    T Param { get; set; }

    /// <summary> Gets or sets the client. </summary>
    /// <value> The client. </value>
    IYagzClient Client { get; set; }

    /// <summary> Gets or sets the service. </summary>
    /// <value> The service. </value>
    YagzService Service { get; set; }

    /// <summary> Constructor. </summary>
    /// <remarks> Simon, 30.12.2009. </remarks>
    /// <param name="service"> The service. </param>
    /// <param name="client">  The client. </param>
    /// <param name="param">   The parameter. </param>
    public YagzClientAsyncCall(YagzService service, IYagzClient client, T param)
    {
        Param = param;
        Client = client;
    }

    /// <summary>   
    /// Invokes the client callback. If a timeout exception occurs, 
    /// the client will be removed from clients' list.
    /// </summary>
    /// <remarks> Simon, 30.12.2009. </remarks>
    /// <param name="clientCallback">   The client callback. </param>
    protected void Invoke(Action<T> clientCallback)
    {
        try
        {
            if (clientCallback != null)
            {
                clientCallback(Param);
            }
        }
        catch (TimeoutException)
        {
            // Remove the client and the user
            Service.RemoveClient(Client);
        }
    }

    protected void Invoke(object objCallback)
    {
        Invoke(objCallback as Action<T>);
    }

    public void CallOperationAsync(Action<T> clientCallback)
    {
        ParameterizedThreadStart ts = new ParameterizedThreadStart(this.Invoke);
        Thread t = new Thread(ts);
        t.Start(clientCallback);
    }
}

假设以下代码是通知聊天室客户端写入新消息的方法的一部分:

foreach (User user in chatNode)
{
     // Don't notify the current client. Deadlock!
     if (!user.Equals(currentUser))
     {
         IYagzClient client = GetClientByUser(user);

         if (client != null)
         {
             var asyncCall = new YagzClientAsyncCall<Message>(this, client, message);
             asyncCall.CallOperationAsync(client.OnChatMessageReceived);
         }
     }
 }

我只是创建一个新的YagzClientAsyncCall-Object,并在新线程上调用该操作。

相关问题