在silverlight3中对wcf的双工通信有了很多的进展,当然是相对我大半年前研究这技术点的时候,还记得当时一系列问题都是无法得到解决的,使用上与常规的wcf也有着很大的区别,没有对wcf有深入理解的朋友是不可以用当时的它来开发实际的项目的,像序列化、直接抽像方法名等都是令人头痛的问题,所以一直不想提及sl中的此技术。
直到现在sl3的降临把当时的一系列问题的解决了,而且性能也有所提高,应用的时候和传统的wcf有了相当的相似度,那么不值得说述一下了。
以下演示的是silverlight3中使用wcf的pulling duplex的一个比效简单的双工应用,本来是想直接做一个聊天室的应用,发现还有一些问题是没有解决的,所以先写下这个简单的应用,以后待问题解决后再作为应用来讲述这个技术。
1.我们的 Wcf 服务将会使用 Silverlight 3 SDK 附带的 PollingDuplex。
要添加对 System.ServiceModel.PollingDuplex 程序集的引用:
2.WCF 服务和回调合同的创建
创建 Silverlight 启用 WCF 服务和 回调契约。
回调契约是用于 WCF 服务向 Silverlight 客户端推送数据的接口:
1: public interface IDuplexServiceCallback
2: {
3: [OperationContract(IsOneWay = true)]
4: void GetData(string data);
5: }
请注意 GetData 方法是用 OperationContract 属性标识的,并且 IsOneWay 参数设置为 true。
3.使用回调契约链接 WCF 服务。
用标识为 ServiceContract 属性的WCF服务,并将 CallbackContract 设置为有效的类型:
1: [ServiceContract(Namespace = "", CallbackContract = typeof(IDuplexServiceCallback))]
2: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
3: public class Service1
4: {
5: [OperationContract]
6: public void DoWork()
7: {
8: //.............
9: }
10: }
4.将数据推送到客户端之调用回调合同方法。
要将数据发送到客户端,我们首先必须获得一个有效的回调通道和调用的方法之一。
在本例中我们通过调用 OperationContext.Current.GetCallbackChannel <iduplexservicecallback>获取回调通道)
并调用它的 GetData() 方法把数据推送至所有在线的客户端:
1: [ServiceContract(Namespace = "", CallbackContract = typeof(IDuplexServiceCallback))]
2: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
3: public class Service1
4: {
5: //我使用了这个实例来存放所有的已成功连接服务器端的客户端
6: static List<IDuplexServiceCallback> cilents = new List<IDuplexServiceCallback>();
7:
8: [OperationContract]
9: public void DoWork()
10: {
11: cilents.Add(OperationContext.Current.GetCallbackChannel<IDuplexServiceCallback>());
12:
13: for (int i = 0; i <= cilents.Count - 1; i++)
14: {
15: cilents[i].GetData("new user logined");
16: }
17: }
18: }
5.创建Service Host Factory。
由于目前不支持 web.config 中配置服务。
我们要手动去创建 Service Host Factory 提供服务终结点配置:
1: public class PollingDuplexServiceHostFactory : ServiceHostFactoryBase
2: {
3: public override ServiceHostBase CreateServiceHost(string constructorString,
4: Uri[] baseAddresses)
5: {
6: return new PollingDuplexSimplexServiceHost(baseAddresses);
7: }
8: }
9:
10: class PollingDuplexSimplexServiceHost : ServiceHost
11: {
12: public PollingDuplexSimplexServiceHost(params System.Uri[] addresses)
13: {
14: base.InitializeDescription(typeof(jacDuplex.Web.Service1), new UriSchemeKeyedCollection(addresses));
15: base.Description.Behaviors.Add(new ServiceMetadataBehavior());
16: }
17:
18: protected override void InitializeRuntime()
19: {
20: // Add an endpoint for the given service contract.
21: this.AddServiceEndpoint(
22: typeof(jacDuplex.Web.Service1),
23: new CustomBinding(
24: new PollingDuplexBindingElement(),
25: new BinaryMessageEncodingBindingElement(),
26: new HttpTransportBindingElement()),
27: "");
28:
29: // Add a metadata endpoint.
30: this.AddServiceEndpoint(
31: typeof(IMetadataExchange),
32: MetadataExchangeBindings.CreateMexHttpBinding(),
33: "mex");
34:
35: base.InitializeRuntime();
36: }
37: }
最后,让我们应用该Factory到服务:
1: <%@ ServiceHost Language="C#" Debug="true" Service="jacDuplex.Web.Service1" CodeBehind="Service1.svc.cs" Factory="Silverlight.Client.Web.Services.PollingDuplexServiceHostFactory" %>
请注意有运行此服务之前请先把web.config文件中所有wcf相关的配置都删除。
7. 在Silverlight 项目添加服务引用 。
请注意我们要添加双工服务的引用时 ServiceReferences.ClientConfig 不会产生内容,因此我们必须手动将端点和地址的配置传递给服务客户端:
1: EndpointAddress address = new EndpointAddress("http://localhost:2244/Service1.svc");
2:
3: CustomBinding binding = new CustomBinding(
4: new PollingDuplexBindingElement(),
5: new BinaryMessageEncodingBindingElement(),
6: new HttpTransportBindingElement());
7:
8: ServiceReference1.Service1Client client = new jacDuplex.ServiceReference1.Service1Client(binding, address);
8. 使用服务
使用双工的服务非常简单。
我们只要处理 GetDataReceived 事件 (因为我们的回调契约公开的是 GetData 方法),并让 WCF 处理客户端/服务器通信
上图中,一共有两个客户端,每个客户端登陆时都会通知其他所有客户端有一个新的用户登陆。
结束语:
尽管目前silverlight 3中对wcf的双式支持还未达到完美的境地,但相对之前已经是一个飞跃的进步。剩下的一个些问题还有待研究去决解,相信那只是我对它未有更加深入的了解而产生的疑问,相信很快会得到解决,例如:捕抓异常的客户端等。