WCF - 客户端回调与“保留订阅者列表”的轮询

时间:2009-12-07 22:18:33

标签: c# .net wcf

我想在WCF中创建一个简单的客户端 - 服务器示例。我做了一些回调测试,到目前为止工作正常。我用以下界面玩了一下:

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IStringCallback))]
public interface ISubscribeableService
{
    [OperationContract]
    void ExecuteStringCallBack(string value);

    [OperationContract]
    ServerInformation Subscribe(ClientInformation c);

    [OperationContract]
    ServerInformation Unsubscribe(ClientInformation c);
}

这是一个简单的例子。稍微调整一下。您可以要求服务器“执行字符串回调”,在这种情况下,服务器会反转字符串并调用所有已订阅的客户端回调。

现在,问题出现了:如果我想实现一个系统,其中所有客户端都“注册”服务器,并且服务器可以“询问”客户端它们是否还活着,你会用回调来实现吗(所以而不是这个“stringcallback”一种TellTheClientThatIAmSti​​llHereCallback)。通过检查回调上的通信状态,我也可以“知道”客户端是否已经死亡。类似的东西:

Subscribers.ForEach(delegate(IStringCallback callback)
                    {
                        if (((ICommunicationObject)callback).State == CommunicationState.Opened)
                        {
                            callback.StringCallbackFunction(new string(retVal));
                        }
                        else
                        {
                            Subscribers.Remove(callback);
                        }
                    });

我的问题,换句话说:

  • 服务器可能有3个客户端
  • 客户端A死亡(我拔下笔记本电脑的插头)
  • 服务器终止并重新上线
  • 新客户出现

所以基本上,你会使用回调来验证客户的“仍然生存状态”吗,或者你会使用民意调查并跟踪“我没听说过客户多久”......

4 个答案:

答案 0 :(得分:3)

您可以通过Closed的{​​{1}},ClosingFaulted事件检测连接状态的大多数更改。您可以在设置回调的同时挂钩它们。这绝对比民意调查好。

IIRC,ICommunicationObject事件只会在之后触发实际尝试使用回调(失败)。因此,如果客户端刚刚消失 - 例如,硬重启或关机 - 那么您将不会立即得到通知。但你需要做什么?如果是这样,为什么?

WCF回调可能随时失败,您始终需要将此保留在脑海中。即使客户端和服务器都很好,由于异常或网络中断,您仍可能最终出现故障通道。或者客户端可能会在您上次轮询和当前操作之间的某个时间离线。关键是,只要您在防御性地编写回调操作(这无论如何都是好的做法),那么挂钩上面的事件对于大多数设计来说通常就足够了。如果由于任何原因发生错误 - 包括客户端未能响应 - 则Faulted事件将启动并运行清理代码。

这就是我所说的被动/懒惰方法,与轮询或保持活动的方法相比,需要更少的编码和网络聊天。

答案 1 :(得分:2)

如果启用可靠会话,WCF将在内部维护保持活动控制机制。它通过隐藏的基础设施测试消息定期检查另一端是否还在那里。可以通过ReliableSession.InactivityTimeout属性来影响这些检查的时间间隔。如果将属性设置为20秒,则在另一方发生服务故障后,ICommunicationObject.Faulted事件将在大约20到30(最长)秒内被提升。

如果您想确保客户端应用程序始终保持“自动连接”,即使在临时服务中断后,您可能还想使用反复尝试创建新代理实例的工作线程(来自线程池)客户端,并在Faulted事件被提升之后调用会话启动操作。

作为第二种方法,既然您正在实现工作线程机制,您也可以忽略Faulted事件并让工作线程在客户端应用程序的整个生命周期内循环。您让线程重复检查代理状态,并在状态出现故障时尝试进行修复。

使用第一种或第二种方法,您可以实现服务总线体系结构(中介模式),保证所有客户端应用程序实例随时准备好在服务运行时接收“自发”服务消息。

当然,这只有在可靠的会话“如此”配置正确才能开始时(使用支持会话的绑定,并应用ServiceContractAttribute.SessionMode,ServiceBehaviorAttribute.InstanceContextMode,OperationContractAttribute.IsInitiating和OperationContractAttribute.IsTerminating)有意义的财产)。

答案 2 :(得分:0)

我有类似的情况使用WCF和回调。我不想使用轮询,但我使用的是“reilable”协议,所以如果客户端死了,那么它会挂起服务器,直到它超时并崩溃。

我不知道这是否是最正确或最优雅的解决方案,但我所做的是在服务中创建一个表示客户端代理的类。该类的每个实例都包含对客户端代理的引用,并且只要服务器设置了类的“message”属性,它就会执行回调函数。通过这样做,当客户端断开连接时,单个包装器类将获得超时,并从服务器的侦听器列表中删除自己,但服务不必等待它。这实际上并没有回答你关于确定客户端是否还活着的问题,但这是另一种将服务结构化为添加者问题的方法。如果您需要知道客户端何时死亡,那么当客户端包装器将自己从侦听器列表中删除时,您将能够接听。

答案 3 :(得分:0)

我没有尝试通过线路使用WCF回调,但我已经将它们用于进程间通信。我遇到了一个问题,即调用的调用正在同一个线程上结束,并且当有些调用依赖于同一个线程时,使服务死锁。

这可能适用于您目前遇到的问题,所以我必须采取以下措施来解决问题。

将此属性放在WCF服务器实现类

的服务器和客户端上
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class WCFServerClass

ConcurrencyMode.Multiple在其自己的线程上进行每个调用进程,这可以帮助您在客户端终止时锁定服务器直到超时。

我还确保在客户端使用线程池以确保客户端没有线程问题