September 24, 2008
@ 07:14 PM

I received a question regarding this post on WCF and what my handlers look like when a client disconnects (either because of a fault or the client connection is closed). It's fairly simple. Here's the code used to hook up the events:

IClientCallback remoteMachine = OperationContext.Current.GetCallbackChannel<IClientCallback>();

OperationContext.Current.Channel.Faulted += new EventHandler(ClientFaulted);

OperationContext.Current.Channel.Closed += new EventHandler(ClientClosed);

 

As a side note, I haven't quite gotten in the habit of using the new/shortened syntax for hooking up delegates. The code above can actually now be written as:

IClientCallback remoteMachine = OperationContext.Current.GetCallbackChannel<IClientCallback>();

OperationContext.Current.Channel.Faulted += ClientFaulted;

OperationContext.Current.Channel.Closed += ClientClosed;

 

At any rate, the code in both handlers is actually the same, so I'll just show ClientClosed:

/// <summary>

 /// Called whenever a client machine's connection is closed.

 /// Automatically removes them from our internal list of clients.

 /// </summary>

 /// <param name="sender"></param>

 /// <param name="e"></param>

 void ClientClosed(object sender, EventArgs e)

{

    IClientCallback remoteMachine = sender as IClientCallback;

 

    this.RemoveClientMachine(remoteMachine);           

}

 

All it does is cast the sender to the IClientCallback interface and call another method which actually removes it from my internal list. Here's what that code is doing (actually, I send out another notification in the real code to any other clients to let them know something has changed). It just locks the list then uses a lambda to find the client in the list, and if it's found, it's removed.

private void RemoveClientMachine(IClientCallback remoteMachine)

{

    if (remoteMachine != null)

    {

        RegisteredClient client;

 

        // Unregister them automatically

        lock (m_callbackList)

        {

            client = m_callbackList.Find(c => c.CallBack == remoteMachine);

 

            if (client != null)

                m_callbackList.Remove(client);                   

        }

 

One interesting failure scenario I found occurred when you had a large number of clients connected and something like your main network line goes down. In some cases I wouldn't receive a notification for every client to remove them from a list (I'm guessing it was firing so many events some of them were being lost). At any rate, the easiest way for me to address this was to include a watchdog timer which would periodically sweep through the connections and attempt to determine if they were still valid. Here's what that looks like:

public void CheckCallbackChannels()

{

    RegisteredClient[] clientList = new RegisteredClient[0];

 

    lock (m_callbackList)

    {

        clientList = new RegisteredClient[m_callbackList.Count];

        m_callbackList.CopyTo(clientList);

 

        foreach (RegisteredClient registeredClient in clientList)

        {

            ICommunicationObject callbackChannel = registeredClient.CallBack as ICommunicationObject;

 

            if (callbackChannel.State == CommunicationState.Closed || callbackChannel.State == CommunicationState.Faulted)

            {

                this.RemoveClientMachine(registeredClient.CallBack);                       

            }

        }

    }                       

}

 

Links:

 http://www.rcs-solutions.com/blog/2008/07/06/WCFNotificationOnDisconnect.aspx


 
Name
E-mail
(will show your gravatar icon)
Home page

Comment (Some html is allowed: a@href@title, b, i, strike) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview