WCF Notification on Disconnect

本文介绍如何在WCF中处理客户端突然断开的情况,包括使用事件监听客户端断开、从内部列表移除断开的客户端及通过定时检查清理无效连接的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

            }

        }

    }                       

}

 

I've been working on an application which relies heavily on WCF to communicate. I have a very simple interface (contract) that the server supports to connect and disconnect: it exposes the methods Register() and Unregister(). When the client starts up and connects, it calls the Register() method, passing in some identification info. I save this information in a List<T> so that it can be used by the server to provide callbacks. When the client disconnects, it calls Unregister() which then removes the client from the collection.

That works well enough, right up until a client abruptly disconnects (ex. a connection error, network line goes down, etc.) Then suddenly I end up with a reference to a disconnected client on the server. If I attempt to use this connection to send a message back to the client an exception is thrown (which still works OK). It would be nice to know immediately as soon as a client disconnects.

If you are using TCP (full duplex) as the communication channel, you can do this pretty easily - note: this doesn't work for some of the other communication modes, like HTTP duplex. For those, you might have to rely on the various timeout settings in the .config file.

Here's what that ends up looking like (I happen to be taking advantage of LINQ here, but you can easily adjust that code if you're using an older vesion of the framework):

  203 private void IServer.Register(string systemName)

  204 {

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

  206     // Hook up events to let us know if the client disconnects w/o telling us.

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

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

  209 

  210     // Get object reference to figure out the IP address of the connected client

  211     MessageProperties prop = OperationContext.Current.IncomingMessageProperties;

  212     RemoteEndpointMessageProperty endpoint = prop[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;

  213 

  214     // (threading/locking code removed)

  215 

  216     client = m_callbackList.Find(c => c.SystemName == systemName);

  217     if (client == null)

  218     {

  219         // It's not already on our list, add it

  220         m_callbackList.Add(new RegisteredClient(systemName, endpoint.Address, remoteMachine));

  221     }

  222 }

 

When the client disconnects, the ClientFaulted/ClientClosed events will be fired. Honestly, at this point, the Unregister method isn't really needed, assuming you follow the same disconnection process in both cases.
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值