代码之家  ›  专栏  ›  技术社区  ›  Simon

WCF双工通道:检查回调通道是否仍然可用

  •  2
  • Simon  · 技术社区  · 16 年前

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

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

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

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

    如何检查客户是否仍在倾听?顺便说一句,在客户机上调用的操作都是单向声明的,并发模式设置为“多个”。

    谢谢大家!

    问候,

    西蒙

    3 回复  |  直到 13 年前
        1
  •  8
  •   Gokhan Demir    14 年前

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

        2
  •  1
  •   Kwal    16 年前

    通信对象(即回调通道)上存在关闭和出错的事件。您可能需要为这些添加处理程序,并跟踪哪些客户端仍然有有效的可用通道。

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

        3
  •  1
  •   Gustavo Mori    13 年前

    主要的问题是我没有得到任何例外,除了 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对象,并让操作在一个新线程上被调用。

    推荐文章