【问题标题】:WCF Duplex Channel: Check if callback channel is still availableWCF 双工通道:检查回调通道是否仍然可用
【发布时间】:2012-08-07 08:41:12
【问题描述】:

我有以下问题。我正在写聊天软件。客户端/服务器机制基于 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”。

谢谢大家!

你好,

西蒙

【问题讨论】:

    标签: wcf client duplex-channel


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      CommunicationObject(即回调通道)上有关闭和故障的事件。您可能希望为这些添加处理程序并跟踪哪些客户端仍然有可用的有效通道。

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

      【讨论】:

        【解决方案3】:

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

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

        这是一个代码 sn-p,显示了我是如何做到的:

        起初我创建了一个类来代表对客户端的一次调用:

        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 并让操作在新线程上调用。

        【讨论】:

        • 虽然这可能有效,但像这样按需创建线程通常是不好的做法。您最终可能会得到 1000 个不需要的线程。最好使用固定数量较少的线程来进行阻塞收集。
        猜你喜欢
        • 2010-12-17
        • 1970-01-01
        • 2010-09-30
        • 1970-01-01
        • 2017-11-24
        • 1970-01-01
        • 2017-06-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多