WCF duplex channel: check if callback channel is available

I have the following problem. I am writing a chat program. The client / server mechanism is based on the DualHttpBinding for WCF. This means that if a user sends a message, all clients that are in the room where the message was sent are notified by the server.

I want to make sure that if the client application crashes (why not) the client object is removed from the room lists.

Is it possible to check the state of the callback channel before calling the callback operation? The problem is, if I call an operation on a client that is no longer connected (due to an unexpected failure), 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 the client is still listening? BTW, the operations called on the client are declared by OneWay and the ConcurrencyMode is set to Multiple.

Thanks everyone!

Greetings,

Simon

+2


source to share


3 answers


The main problem was that I didn't have any exceptions other than TimeoutException . My service was blocked for 1 minute (timeout I set) until an exception was thrown.

I resolved this issue with the following workaround. Instead of calling the client callback operation on the current service worker thread, I created a new thread that calls the client callback operation and waits for a TimeoutException. If a timeout occurs, the user is simply removed from the chat lists to which they belonged.

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

First, I created a class representing one 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 chat clients that a new message has been 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.

+1


source


You can contract the ICommunicationObject callback and then check the status of the channel.



+8


source


There are events on the CommunicationObject (i.e. the callback channel) for Closed and Faulted. You can add handlers to these and keep track of which clients still have an available channel.

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

+1


source







All Articles