WCF Duplex Channel: Check if callback channel is s

2019-05-19 21:37发布

I have the following problem. I'm writing chat software. The client/server mechanism is based on DualHttpBinding of WCF. This means that if a user sends a message, all clients that are in the room where the message has been sent, are notified by the server.

I want to ensure, that if a client's application crashes (whyever), the client object is removed from the rooms' lists.

Is there a possibility to check the callback channel's state before calling a callback operation? The problem is, that if i call an operation on a client which is not anymore connected (because of an unexpected crash), the service will hang.

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

How can i check if a client is still listening? BTW, the operations called on the client are all declared OneWay and the ConcurrencyMode is set to "Multiple".

Thank you all!

Greets,

Simon

3条回答
SAY GOODBYE
2楼-- · 2019-05-19 21:59

There are events on a CommunicationObject (i.e. callback channel) for Closed and Faulted. You may want to add handlers for these and track which clients still have a valid channel available.

You can also take a look at the IChannelInitializer class to implement tracking of clients.

查看更多
地球回转人心会变
3楼-- · 2019-05-19 22:07

The main problem was that I didn't get any exceptions, except from a TimeoutException. My service was blocked for 1 min (the timeout I set), until the exception was fired.

I resolved this problem through the following workaround. Instead of calling the client callback operation on the current working thread of the service, I created a new thread that calls the client callback operation and waits for a TimeoutException. If the timeout occurs, the user is simply removed from the chatroom lists he belonged to.

This is a code snippet that shows how I did it:

At first I created a class representing a single call to the client:

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

Suppose the following code is part of a method that notifies chatroom clients that a new message was written:

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

I just create a new YagzClientAsyncCall-Object and let the operation be called on a new thread.

查看更多
兄弟一词,经得起流年.
4楼-- · 2019-05-19 22:13

You can cast callback contract to ICommunicationObject and then check for the channel state.

查看更多
登录 后发表回答