WCF Duplex Channel: Check if callback channel is still available

11,851

Solution 1

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.

Solution 2

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

Solution 3

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.

Share:
11,851
Simon
Author by

Simon

M. Sc. in Computer Science at KIT (Karlsruhe Institut of Technology) Data Engineer / Software Engineer Freelancer

Updated on June 05, 2022

Comments

  • Simon
    Simon almost 2 years

    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