33

另请参阅“ WCF 通过防火墙推送到客户端

我需要一个连接到 WCF 服务器的 WCF 客户端,然后当服务器上的某些数据发生更改时,客户端需要更新其显示。

因为客户端和服务器之间可能存在防火墙。

  • 所有通信都必须通过 HTTP
  • 服务器无法对客户端进行(物理)传出呼叫。

当我同时编写客户端和服务器时,我不需要将解决方案限制为仅使用肥皂等。


我正在寻找“长轮询”/“彗星”等的内置支持


感谢 Drew Marsh 就如何在 WCF 中实现长轮询提供了最丰富的答案。但是我认为 WCF 的主要“卖点”是您可以通过在配置文件中配置要使用的通道来完成此类事情。 例如,我想要一个逻辑上双向但仅物理传入的通道。

4

5 回答 5

49

听起来你已经知道答案了:使用长轮询。:) 所以我想唯一需要解释的是你如何能够使用 WCF 并以最有效的方式完成此任务。


基础:

  1. 首先,决定您希望每个“长轮询”持续多长时间。为了论证的缘故,我将选择 5 分钟超时。
  2. 在客户端绑定上,更改sendTimeout="00:05:00".
  3. 就像使用 XmlHttpRequest (XHR) 进行长轮询一样,当超时确实发生时,您需要检测它并重新发出下一个轮询请求。这在 WCF 中非常容易,因为有一个特定的异常,TimeoutException您可以捕获它以轻松检测到这是问题与其他一些异常。
  4. 根据您托管 WCF 服务的方式,您需要确保将自己配置为允许处理长达 5 分钟。从纯 WCF 的角度来看,您需要确保将receiveTimeout="00:05:00". 但是,如果您在 ASP.NET 内部进行托管,您还需要配置 ASP.NET 运行时以使用更高的超时时间<httpRuntime executionTimeout="300" />注意:此属性的测量值以秒为单位)。

在客户中高效

如果您只是将客户端设置为同步调用服务并且客户端在等待响应时阻塞 5 分钟,那么这不是对系统资源的非常有效的使用。您可以将这些调用放在后台线程上,但这仍然会在调用未完成时消耗线程资源。处理这个问题的最有效方法是使用异步操作。

如果您手动创建服务合同,我建议您查看MSDN 上OperationContractAttribute.AsyncPattern的此部分,了解有关如何为每个调用添加BeginXXX/ EndXXXasync 方法对的详细信息。但是,如果您正在使用svcutil为您生成操作合同,则生成异步方法所需要做的就是/async在命令行上传递选项。有关此主题的更多详细信息,请查看 MSDN 上的同步和异步主题

现在您已经定义了异步操作,该模式非常类似于使用 XHR。您调用BeginXXX传递委托AsyncCallback方法。该BeginXXX方法将返回一个IAsyncResult,如果您希望能够等待操作(在更高级的场景中)或忽略,您可以保留它,然后 WCF 基础结构将异步发送请求到服务器并等待幕后的回应。当收到响应发生异常时,将调用您传递给BeginXXX方法的回调。在此回调方法中,您需要调用EndXXX传递给您的相应方法IAsyncResult。在通话期间EndXXX方法,您需要使用异常处理来处理调用该方法时可能发生的任何类型的逻辑错误,但这也是您现在能够捕获TimeoutException我们之前讨论过的地方。假设您得到了良好的响应,数据将从EndXXX调用中返回,您可以以任何有意义的方式对该数据做出反应。

注意:关于这种模式要记住的一件事是线程的性质。来自 WCF 的异步回调将在托管线程池的线程上接收。如果您计划使用 WPF 或 WinForms 等技术更新 UI,则需要确保使用InvokeorBeginInvoke方法将调用编组回 UI 线程。

在服务器上高效

如果我们要担心客户端的效率,那么当涉及到服务器时,我们应该加倍担心。显然,这种方法对服务器端提出了更多要求,因为连接必须保持打开和挂起状态,直到有理由将通知发送回客户端。这里的挑战是您只想将 WCF 运行时与实际发送事件的那些客户端的处理联系起来。其他一切都应该只是睡着了,等待事件发生。幸运的是,我们刚刚在客户端使用的相同异步模式也适用于服务器端。但是,现在有一个主要区别:现在必须从WCF 运行时将等待发出信号的方法中返回IAsyncResult(因此 a WaitHandle),然后再调用您的BeginXXXEndXXX方法。

除了我之前已经提供的链接之外,您不会在 MSDN 中找到太多的文档方式,不幸的是,它们关于编写异步服务的示例不太有用。也就是说, Wenlong Dong前段时间写了一篇关于使用异步模型扩展 WCF 服务的文章,我强烈建议您查看。

除此之外,老实说,我不能就如何最好地为您在服务器端实现异步模型提供太多建议,因为这完全取决于您的事件首先来自哪种数据源。文件输入/输出?消息队列?数据库?其他一些具有自己的消息服务的专有软件,您正试图为其提供外观?我不知道,但他们都应该提供自己的异步模型,您可以在其上搭载自己的服务以使其尽可能高效。

更新处方

由于这似乎是一个流行的答案,我想我应该回到这里并根据最近的形势变化提供更新。在这一点上,现在有一个名为SignalR的 .NET 库,它提供了这个确切的功能,并且绝对是我推荐的实现与服务器的任何此类通信的方式。

于 2009-11-17T03:53:55.853 回答
2

如果服务器可以与服务总线建立传出连接,您可以实现一种类型的回调。这样,客户端/服务器根本不需要相互了解,只需要依赖服务总线。请参阅.NET 服务总线

您将需要查看WSDualHttpBinding

WSDualHttpBinding 为 Web Service 协议提供与 WSHttpBinding 相同的支持,但用于双工协定。WSDualHttpBinding 仅支持 SOAP 安全性并需要可靠的消息传递。此绑定要求客户端具有为服务提供回调端点的公共 URI。这是由 ClientBaseAddress 提供的。双重绑定将客户端的 IP 地址暴露给服务。客户端应该使用安全性来确保它只连接到它信任的服务。

于 2009-11-09T19:31:33.813 回答
2

虽然不是 WCF,但您可以尝试使用 XMPP 来实现该功能。InfoQ上有一篇关于它和其他系统的文章。虽然文章指出 XMPP 不能通过 HTTP 使用,但您可以在使用BOSH时使用。

有 .NET 库可用agsXMPP来命名。

我工作的公司开始使用它将更新通知推送到应用程序以刷新部分用户界面。

于 2009-11-12T13:12:25.247 回答
1

谷歌搜索“ WCF 双工”。我已经使用 netTcpBinding(跨大洲)成功地使用了它,但我不确定 basicHttpBinding。

但是,它确实需要服务器回调客户端。如果服务器不允许这样做,轮询可能是您唯一的选择......

于 2009-11-09T13:23:38.593 回答
0

如果服务器无法调用客户端(通常不应该),您应该让客户端按照您的指定轮询服务器。因为您已经有了 WCF 基础,所以只需为此添加一个操作。

于 2009-11-09T13:21:08.980 回答