问题描述
我需要一个连接到 WCF 服务器的 WCF 客户端,然后当服务器上的某些数据更改时,客户端需要更新其显示.
I need to have a WCF client that connect to a WCF server, then when some of the data changes on the server the clients need to update its display.
因为客户端和服务器之间可能存在防火墙.
As there is likely to be a firewall between the clients and the server.
- 所有通信都必须通过 HTTP
- 服务器无法向客户端发出(物理)传出调用.
当我同时编写客户端和服务器时,我不需要将解决方案限制为仅使用肥皂等.
我正在寻找长轮询"/"等
感谢 Drew Marsh 关于如何在 WCF 中实现长轮询的最翔实的回答.但是我认为 WCF 的主要卖点"是你可以通过配置要在配置文件中使用的通道来做这种事情.例如,我想要一个逻辑上有两种方式但仅物理传入的频道.
Thanks for the most informative answer from Drew Marsh on how to implement long polling in WCF. However I thought the main "selling point" of WCF was that you could do this sort of thing just by configuring the channels to be used in the config file. E.g I want a channel that logically two way but physically incoming only.
推荐答案
在我看来,您似乎已经知道答案:使用长轮询.:) 所以我想唯一需要解释的就是如何使用 WCF 以最有效的方式实现这一点.
It sounds to me like you already know the answer: use long polling. :) So I guess the only thing left to explain is how you might be able to accomplish this with WCF and in the most efficient manner possible.
- 首先,确定您希望每个长轮询"持续多长时间.为了争论起见,我将选择 5 分钟超时.
- 在客户端绑定上,更改
sendTimeout="00:05:00"
. - 就像使用 XmlHttpRequest (XHR) 进行长轮询一样,当超时确实发生时,您需要检测它并重新发出下一个轮询请求.这在 WCF 中很容易,因为有一个特定的异常,
TimeoutException
,您可以捕获以轻松检测到这是问题而不是其他一些异常. - 根据您托管 WCF 服务的方式,您需要确保将自己配置为允许处理长达 5 分钟.从纯 WCF 的角度来看,您需要确保设置了
receiveTimeout="00:05:00"
.但是,如果您在 ASP.NET 内部托管,则还需要将 ASP.NET 运行时配置为具有更高的超时时间,这是使用<httpRuntime executionTimeout="300"/>
(注意:此属性的测量值以秒为单位).
- First, decide how long you want each "long poll" to be. For argument's sake I'm going to choose 5min timeouts.
- On the client side binding, change the
sendTimeout="00:05:00"
. - Just like using XmlHttpRequest (XHR) for long polling, when the timeout does actually occur, you will need to detect it and re-issue the next polling request. This is quite easy in WCF because there is a specific exception,
TimeoutException
, that you can catch to easily detect this was the issue vs. some other exception. - Depending on how you're hosting your WCF service, you will need to make sure to configure yourself to allow processing for up to 5mins. From a pure WCF perspective you'll want to make sure you set the
receiveTimeout="00:05:00"
. However, if you're hosting inside of ASP.NET you will also need to configure the ASP.NET runtime to have a higher timeout which is done using the<httpRuntime executionTimeout="300" />
(note: the measurements are in seconds for this attribute).
对客户有效率
如果您只是将客户端设置为同步调用服务,并且客户端在等待响应时阻塞 5 分钟,那么这不是非常有效地使用系统资源.您可以将这些调用放在后台线程上,但在调用未完成时仍会消耗线程资源.处理此问题的最有效方法是使用异步操作.
Being efficient in the client
If you just setup your client to synchronously call the service and the client blocks for 5mins while waiting for a response, that's not a very efficient use of system resources. You could put these calls on background threads, but that's still going to chew up a thread resource while the call is outstanding. The most efficient way to deal with this is to use async operations.
如果您手动创建服务合同,我建议您查看 MSDN 上 OperationContractAttribute.AsyncPattern
的这一部分,详细了解如何添加 BeginXXX
/EndXXX
异步每个调用的方法对.但是,如果您使用 svcutil
为您生成操作合同,则生成异步方法所需要做的就是在命令行上传递 /async
选项.有关此主题的更多详细信息,请查看 MSDN 上的同步和异步主题.
If you're creating your service contracts by hand, I would suggest checking out this section on MSDN on OperationContractAttribute.AsyncPattern
for details on how to add a BeginXXX
/EndXXX
async method pair for each of your calls. However, if you're using svcutil
to generate your operation contracts for you, all you need to do to have async methods generated is pass the /async
option on the command line. For more details on this topic, check out the Synchronous and Asynchronous topic on MSDN.
现在您已经定义了异步操作,该模式非常类似于使用 XHR.您调用 BeginXXX
方法,将 anAsyncCallback
委托.BeginXXX
方法将返回一个 IAsyncResult
,如果您希望能够等待操作(在更高级的场景中)或忽略,您可以保留它,然后 WCF 基础结构将异步发送请求到服务器并等待幕后的回应.当收到响应或发生异常时,将调用您传递给 BeginXXX
方法的回调.在此回调方法中,您需要调用相应的 EndXXX
方法,并传入传递给您的 IAsyncResult
.在调用 EndXXX
方法期间,您需要使用异常处理来处理调用该方法时可能发生的任何类型的逻辑错误,但这也是您现在能够捕获的地方我们之前谈到的 TimeoutException
.假设您得到了良好的响应,数据将从 EndXXX
调用返回,您可以以任何有意义的方式对该数据做出反应.
Now that you've go your async operations define, the pattern is very much like working with XHR. You call the BeginXXX
method to which you pass an AsyncCallback
delegate. The BeginXXX
method will return you an IAsyncResult
, which you can either hold onto if you wanted to be able to wait on the operation (in more advanced scenarios) or ignore, and then the WCF infrastructure will asynchronously send the request to the server and wait for a response behind the scenes. When a response is received or an exception occurs, the callback you passed into the BeginXXX
method will be invoked. Inside of this callback method you need to call the corresponding EndXXX
method passing in the IAsyncResult
that is handed to you. During the call to the EndXXX
method you need to employ exception handling to deal with any kind of logical fault that may have occurred while calling the method, but this is also where you'd now be able to catch the TimeoutException
we talked about earlier. Assuming you got a good response, the data will be the returned from the EndXXX
call and you can react to that data in whatever way makes sense.
注意:关于这种模式,需要记住的一件事是线程的性质.来自 WCF 的异步回调将在来自托管线程池.如果您计划使用 WPF 或 WinForms 等技术更新 UI,则需要确保使用 Invoke
或 BeginInvoke
方法.
NOTE: One thing to keep in mind about this pattern is the nature of the threading. The async callbacks from WCF will be received on a thread from the managed thread pool. If you're planning on updating the UI in a technology such as WPF or WinForms, you need to make sure you marshal the calls back to the UI thread using the Invoke
or BeginInvoke
methods.
如果我们要担心客户端的效率,那么当涉及到服务器时,我们应该加倍如此.显然,这种类型的方法对服务器端提出了更多要求,因为连接必须保持打开和挂起状态,直到有理由将通知发送回客户端.这里的挑战是您只想将 WCF 运行时与那些实际发送事件的客户端的处理联系起来.其他一切都应该只是睡着了,等待事件发生.幸运的是,我们刚刚在客户端使用的相同异步模式也适用于服务器端.但是,现在有一个主要区别:现在您必须返回IAsyncResult
(因此WaitHandle
) 来自 BeginXXX
方法,然后 WCF 运行时将等待在调用您的 EndXXX
方法之前发出信号.
If we're going to be worried about efficiency in the client, we should be doubly so when it comes to the server. Obviously this type of approach puts more demand on the server side because a connection must remain open and pending until there is a reason to send notification back to the client. The challenge here is that you only want to tie the WCF runtime up with the processing of those clients who are actually being sent an event. Everything else should just be asleep, waiting for the event to occur. Luckily the same async pattern we just used on the client side also works on the servers side. However, there is now a major difference: now you must return the IAsyncResult
(and thus a WaitHandle
) from the BeginXXX
method which the WCF runtime will then wait to be signaled on before calling your EndXXX
method.
除了我之前已经提供的链接之外,您不会在 MSDN 内部的文档中找到太多内容,不幸的是,他们关于编写异步服务的示例没有多大用处.也就是说,Wenlong Dong 前段时间写了一篇关于使用异步模型扩展 WCF 服务的文章,我强烈建议您查看.
You will not find much in the way of documentation inside of MSDN other than the links I've already provided earlier and, unfortunately, their samples on writing an async-service are less than useful. That said, Wenlong Dong wrote a piece about scaling WCF services with the async model some time ago that I highly recommend you check out.
除此之外,老实说,我不能就如何最好地在服务器端为您实现异步模型提供太多建议,因为这完全取决于您的事件首先来自哪种数据源.文件输入/输出?消息队列?一个数据库?您试图提供外观的其他具有自己的消息传递服务的专有软件?我不知道,但他们都应该提供自己的异步模型,您可以在该模型上搭载自己的服务以使其尽可能高效.
Beyond this, I honestly I can't give too much advice on how best to implement the asynchronous model on the server side for you bcause it depends entirely on what kind of data source your events will be coming from in the first place. File I/O? A message queue? A database? Some other proprietary software with its own messaging service that you're trying to provide a façade over? I don't know, but they should all offer an async models of their own on which you can piggy back your own service to make it as efficient as possible.
由于这似乎是一个流行的答案,我想我应该回到这里并根据最近的景观变化提供更新.在这一点上,现在有一个名为 SignalR 的 .NET 库,它提供了这个确切的功能,绝对是我推荐的方式实现与服务器的任何此类通信.
Since this seems to be a popular answer, I figured I should come back here and provide an update given the recent changes in the landscape. At this point there is now a .NET library called SignalR which provides this exact functionality and is definitely how I would recommend implementing any such communication with the server.
这篇关于WCF 服务器如何通知 WCF 客户端有关更改的信息?(比简单轮询更好的解决方案,例如彗星或长轮询)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!