通过PollingDuplexHttpBinding来实现双工通讯(从WCF服务端推送消息到客户端) 是比较”旧式”的做法. 在SilverLight4以前的版本中, SilverLight并不支持net.tcp通讯, 所以只能通过包装http通讯来实现.
不过, 毕竟http穿透防火墙的能力无人能及, 所以可能还是会有用到PollingDuplexHttpBinding来实现双工的时候. 下面进入正题.
一. 建立演示项目
在Vs中新建一个SilverLight项目, (我用的是silverLight5, .net 4.5) , 命名为SLHttpPollingDuplexSample, 下一步, 然后选择建立一个新的Web application来托管这个SL项目, web application将被自动命名为SLHttpPollingDuplexSample.Web.
二. 创建服务
2.1 在SLHttpPollingDuplexSample.Web项目上右击->Add new item->Wcf service, 由于是演示项目, 不再改名, 就叫service1.svc. 这时项目中会加入两个新文件: IService.cs和Service1.svc.
将IService1.cs改为如下内容:
namespace SLHttpPollingDuplexSample.Web { [ServiceContract(CallbackContract = typeof(IClientCallback))] public interface IService1 { [OperationContract] void Register(); } public interface IClientCallback { [OperationContract(IsOneWay = true)] void PushMessage(string message); } }
简单看一下这个接口文件:
IService1是服务的接口, 它包括一个Register方法, 当客户端调用该方法时, 就意味着它要向服务器订阅信息, 服务器在推送的时候, 就会把信息推送到这个客户端. (当然现实中还需要一个退订功能, 不过这里只是演示基本功能)
下面的接口IClientCallbak 是回调接口, 当服务端需要向客户端推送消息时, 就是通过这个接口来推送的.
2.2 然后把Service1.svc.cs改成如下内容:
public class Service1 : IService1 { public Service1() { new System.Threading.Thread(PumpMessage).Start(); } private static Dictionary<IClientCallback, byte> _clients = new Dictionary<IClientCallback, byte>(); public void Register() { var c = OperationContext.Current.GetCallbackChannel<IClientCallback>(); if (!_clients.ContainsKey(c)) { lock (_clients) { _clients.Add(c, 0); } } } private void PumpMessage() { var deadClients = new List<IClientCallback>(); while (true) { System.Threading.Thread.Sleep(10000); if (_clients.Count < 1) continue; foreach (var c in _clients.Keys) { try { c.PushMessage(DateTime.Now.ToString("HH:mm:ss")); } catch { deadClients.Add(c); } } if (deadClients.Count > 0) { lock (_clients) { deadClients.ForEach(b => _clients.Remove(b)); } deadClients.Clear(); } } } }
关于这个类:
PumpMessage是实现了一个简单的自动推送消息泵, 它会每隔十秒向客户端发送一次消息(当前时间). 如果发现推送失败, 就会认为这个客户端已经离线, 将它从客户端列表中删除.
_clients是一个静态的客户端列表, 虽然它是一个字典型, 但是它的value部分没有用处, 这里只是用字典的快速检索key功能, 所以value部分统一放了个0.
Register的实现也非常简单明了, 它直接把当前信道加入到_clients中.
2.3 添加引用及web.config配置
PollingDuplex并不是默认的.net的一部分, 所以必须通过增加引用的方式把这个dll文件加入项目.
在项目上右击->add reference->browse,
找到C:\Program Files\Microsoft SDKs\Silverlight\v5.0\Libraries\Server\System.ServiceModel.PollingDuplex.dll.
注意在Client文件中也有一个同名的dll文件, 这里一定要选择Server文件夹.
引用完成之后, 在web.config的<system.serviceModel>节按如下配置:
<system.serviceModel> <bindings> <pollingDuplexHttpBinding> <binding name="pollingDuplexHttpBinding1" duplexMode="MultipleMessagesPerPoll" maxOutputDelay="00:00:00.2"/> </pollingDuplexHttpBinding> </bindings> <services> <service name="SLHttpPollingDuplexSample.Web.Service1"> <host> <baseAddresses> <add baseAddress="http://localhost/SLHttpPollingDuplexSample.Web/Service1.svc"/> </baseAddresses> </host> <endpoint address="" binding="pollingDuplexHttpBinding" bindingConfiguration="pollingDuplexHttpBinding1" contract="SLHttpPollingDuplexSample.Web.IService1"/> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <extensions> <bindingExtensions> <add name="pollingDuplexHttpBinding" type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement,System.ServiceModel.PollingDuplex, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> </bindingExtensions> </extensions> <behaviors> <serviceBehaviors> <behavior name=""> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel>
2.4 发布及跨域权限设置
将SLHttpPollingDuplexSample.Web发布到随便哪个目录下, 然后在IIS中add->application把这个目录加入iis.
然后还需要在网站的根目录下放置一个cross domain policy(clientaccesspolicy.xml)文件以允许SL访问.
<?xml version="1.0" encoding ="utf-8"?> <access-policy> <cross-domain-access> <policy> <allow-from> <domain uri="*"/> </allow-from> <grant-to> <resource path="/" include-subpaths="true"/> <socket-resource port="4502-4506" protocol="tcp" /> </grant-to> </policy> </cross-domain-access> </access-policy>
三. 配置SL端
在SL项目中添加服务引用( 地址:http://localhost/SLHttpPollingDuplexSample.Web/Service1.svc ) , 但是由于vs没有内置对PollingDuplex的支持, 所以生成的ServiceReferences.ClientConfig 是空的. 这里将不使用这个文件, 直接用代码创建endpoint.
本来在SL项目中需要引用System.ServiceModel.PollingDuplex.dll 的客户端版, 但是在我的vs2012中, 添加服务引用以后, 就自动引用了, 我印象似乎vs2010还没有这个功能. 如果在SL项目的Reference中没有看到System.ServiceModel.PollingDuplex, 就需要到上面引用服务端dll的位置, 找到Library\Client文件夹, 引用其下面的System.ServiceModel.PollingDuplex.dll .
在SL的MainPage中放一个文本框txt1和一个按钮btn1, 在按钮btn1的点击事件中:
private void btn1_Click_1(object sender, RoutedEventArgs e) { var address = new EndpointAddress("http://localhost/SLHttpPollingDuplexSample.Web/Service1.svc"); var binding = new PollingDuplexHttpBinding(PollingDuplexMode.MultipleMessagesPerPoll); var proxy = new ServiceReference1.Service1Client(binding, address); proxy.PushMessageReceived += proxy_PushMessageReceived; proxy.RegisterAsync(); } void proxy_PushMessageReceived(object sender, ServiceReference1.PushMessageReceivedEventArgs e) { txt1.Text = e.message; }
把SLHttpPollingDuplexSample.Web项目中自动生成的SLHttpPollingDuplexSampleTestPage.html设为起始项, F5运行吧, 看到SL界面以后, 点击一个按钮btn1, 稍等一会儿, 应该就可以看到文本框中出现了一个时间 , 那就是服务器端推送过来的数据了.
结语: 这种方式只是作为一个备选 , 一般情况下, 如果net.tcp可用, 应该首选net.tcp来进行通讯. 关于通过net.tcp来实现双工, 请参看下一篇文章 SilverLight与WCF服务双工通讯第二篇:Net.Tcp binding
---------------------------------------------
作者:夏狼哉
博客:http://www.cnblogs.com/Moosdau
如需引用,敬请保留作者信息,谢谢