本主题描述了如何创建可与 Silverlight 客户端进行通信的双工 Windows Communication Foundation (WCF) 服务。双工服务将保留到 Silverlight 客户端的回调通道,它允许该服务对此客户端进行调用。双工服务具有许多应用程序,例如,包括用于即时消息传递的聊天服务程序或用于向客户端发送通知的监视服务。本示例提供一项服务,该服务允许客户端按名称对指定数量的产品项进行排序。该服务将模拟订单处理过程,然后使用订单的状态回调客户端。
在双工消息传递中,服务和客户端都公开服务协定,这会指定服务和客户端分别向对方所公开的操作。在此示例中,我们使用下面一对服务协定。客户端将对服务调用 Order 方法,而服务将对客户端调用 Receive 方法。
[ServiceContract(Namespace="Silverlight", CallbackContract = typeof(IDuplexClient))]
public interface IDuplexService
{
[OperationContract]
void Order(string name, int quantity);
}
[ServiceContract]
public interface IDuplexClient
{
[OperationContract(IsOneWay = true)]
void Receive();
}
请注意,CallbackContract 属性用于指定回调客户端的服务协定。
另外请注意,对于作为回调协定一部分的每个客户端方法,IsOneWay 属性在 OperationContractAttribute 中也必须设置为 true。此属性表示这些操作不返回答复消息。
创建 WCF 双工服务以使用 Silverlight
-
打开 Visual Studio 2010。
-
从“文件”菜单中依次选择“新建”、“项目”,从您要在其中进行编码的语言(C# 或 Visual Basic)组中左侧列出的“项目类型”中指定“WCF”。然后,从右侧的“模板”中选择“WCF 服务应用程序”,在下面的“名称”框中将其命名为 DuplexService,再单击“确定”。
-
使用针对 DuplexService 命名空间的下列代码替换 IService1.cs 文件的默认内容。此代码使用 interfaces 来定义服务和客户端所用的协定。
//Interface contracts in the IService1.cs file. namespace DuplexService { [ServiceContract(Namespace="Silverlight", CallbackContract = typeof(IDuplexClient))] public interface IDuplexService { [OperationContract] void Order(string name, int quantity); } [ServiceContract] public interface IDuplexClient { [OperationContract(IsOneWay = true)] void Receive(Order order); } }
请注意,此代码就是在讨论 WCF 服务与 Silverlight 客户端之间的双工通信的本质以及它们为定义所用的消息模式而公开的服务协定时,在本主题的简介中显示的代码。
-
打开 Service1.svc.cs 文件,并将下列 using 语句添加到该文件的顶部。
using System.Threading;
将使用此命名空间的功能来模拟示例双工服务中的实际服务的处理延迟行为。
-
编辑文件 Service1.svc.cs 的内容以定义实施
IDuplexService
协定的OrderService
类。首先,移除DuplexService
命名空间声明内的所有类。然后定义OrderService
类,如下面的代码所示。public class OrderService : IDuplexService { IDuplexClient client; string orderName; int orderQuantity; public void Order(string name, int quantity) { // Grab the client callback channel. client = OperationContext.Current.GetCallbackChannel<IDuplexClient>(); orderName = name; orderQuantity = quantity; // Pretend service is processing and will call client back in 5 seconds. using (Timer timer = new Timer(new TimerCallback(CallClient), null, 5000, 5000)) { Thread.Sleep(11000); } } }
Order
操作模拟执行从在线商店订购商品的过程。接收到订单后,通常会执行服务范围之外的一系列后续操作(例如,检查商品的供应情况或者核对用户信用卡的信息)。完成这些操作后,该服务需要回调客户端(即,将信息“推”到客户端),以通知它订单已成功完成。为模拟此情况,我们使用一个 Timer,它将在两个 5 秒的间隔后调用两次该服务。请注意,使用 OperationContext.GetCallbackChannel(T) 方法来获得类型为
IDuplexClient
的回调通道,这可用于在实现和运行 Silverlight 客户端后联系该客户端。 -
在
DuplexService
命名空间中,添加一个Order
类和一个辅助的OrderStatus
枚举,以表示该服务可以将订单相关信息发送回客户端这一信息。public class Order { public OrderStatus Status { get; set; } public List<string> Payload { get; set; } } public enum OrderStatus { Processing, Completed }
-
在
OrderService
类中,添加下列内容。bool processed = false; private void CallClient(object o) { Order order = new Order(); order.Payload = new List<string>(); // Process order. if (processed) { // Pretend service is fulfilling the order. while (orderQuantity-- > 0) { order.Payload.Add(orderName + orderQuantity); } order.Status = OrderStatus.Completed; } else { // Pretend service is processing payment. order.Status = OrderStatus.Processing; processed = true; } // Call client back. client.Receive(order); }
调用该方法两次以模拟服务上的订单处理活动。第一次调用时,它将报告正在处理订单,第二次调用时,它将报告已完成订单并填充订单的
Payload
属性。 -
若要将 DuplexService.OrderService 指定为 Service1.svc 文件中的服务,请右击“解决方案资源管理器”,选择“查看标记”,并粘贴以下服务指令以覆盖默认指令。
// Directive contained in the Service1.svc file. <%@ServiceHost language="c#" Debug="true" Service="DuplexService.OrderService" CodeBehind="Service1.svc.cs "%>
现在已经开发了服务的服务协定,接下来我们必须在 Silverlight 4 可识别的两种双工传输方法之间进行选择。
使用 PollingDuplexHttpBinding
-
在“解决方案资源管理器”中右击“DuplexService”项目,然后选择“添加引用”。单击“浏览”选项卡,并导航到 System.ServiceModel.PollingDuplex.dll 程序集(位于 %ProgramFiles%\Microsoft SDKs\Silverlight\v4.0\Libraries\Server\ 目录中),选择该程序集并单击“确定”。
注意: 请注意,Silverlight 版本 4 SDK 附带两个名为 System.ServiceModel.PollingDuplex.dll 的程序集。WCF 双工服务使用其中一个程序集(位于 %ProgramFiles%\Microsoft SDKs\Silverlight\v4.0\Libraries\Server\ 目录下),也就是您刚才添加了对它的引用的程序集。另一个程序集(位于 %ProgramFiles%\Microsoft SDKs\Silverlight\v4.0\Libraries\Client\ 目录下)在 Silverlight 双工客户端中使用。务必按前面的步骤正确操作以到达正确位置,这样您才能为双工服务引用正确的程序集,并且随后为双工客户端应用程序引用正确的程序集。 -
在“解决方案资源管理器”中右击 Web.config 文件,然后选择“打开”以编辑此配置。首先,通过在 <system.serviceModel> 节中添加以下代码,注册 Silverlight 4 SDK 中的 pollingDuplexHttpBinding。
<!-- Register the binding extension from the SDK. --> <extensions> <bindingExtensions> <add name= "pollingDuplexHttpBinding" type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement,System.ServiceModel.PollingDuplex, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </bindingExtensions> </extensions>
-
在 <bindings> 节中添加 pollingDuplexHttpBinding 元素以指定使用此绑定。
<!-- Create the polling duplex binding. --> <pollingDuplexHttpBinding> <binding name="multipleMessagesPerPollPollingDuplexHttpBinding" duplexMode="MultipleMessagesPerPoll" maxOutputDelay="00:00:07"/> </pollingDuplexHttpBinding>
-
然后,在 <services> 节中指定服务终结点。
<services> <service name="DuplexService.OrderService" behaviorConfiguration="DuplexService.OrderServiceBehavior"> <!-- Service Endpoints --> <endpoint address="" binding="pollingDuplexHttpBinding" bindingConfiguration="multipleMessagesPerPollPollingDuplexHttpBinding" contract="DuplexService.IDuplexService"> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services>
-
现在,Web.config 文件中的 <system.serviceModel> 节应包含以下配置元素。
<!-- Register the binding extension from the SDK. --> <extensions> <bindingExtensions> <add name="pollingDuplexHttpBinding" type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement, System.ServiceModel.PollingDuplex, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </bindingExtensions> </extensions> <!-- Create the polling duplex binding. --> <bindings> <pollingDuplexHttpBinding> <binding name="multipleMessagesPerPollPollingDuplexHttpBinding" duplexMode="MultipleMessagesPerPoll" maxOutputDelay="00:00:07"/> </pollingDuplexHttpBinding> </bindings> <services> <service name="DuplexService.OrderService"> <!-- Service Endpoints --> <endpoint address="" binding="pollingDuplexHttpBinding" bindingConfiguration="multipleMessagesPerPollPollingDuplexHttpBinding" contract="DuplexService.IDuplexService"> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <behaviors> <serviceBehaviors> <behavior> <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --> <serviceMetadata httpGetEnabled="true"/> <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> </system.serviceModel> <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --> <serviceMetadata httpGetEnabled="true"/> <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel>
-
现在已准备好,可以在 Visual Studio 中测试双工服务了。在“解决方案资源管理器”中选择并右击 Service1.svc 文件,然后选择“在浏览器中查看”(或按 Ctrl + F5)以显示该服务的测试页。您应会看到 OrderService 服务测试页,这样便可确定该服务可用。
使用 NetTcpBinding
-
在继续操作之前,确保您正在使用管理员特权运行 Visual Studio。
-
打开 Web.config 文件,添加一个 <netTcpBinding> 元素,该元素带有一个嵌套的 <security> 元素(其 mode 设置为 None)。当前版本的 Silverlight 不支持具有安全性的 NetTcpBinding。
<bindings> <netTcpBinding> <binding name="tcpBindingNoSecurity"> <security mode="None" /> </binding> </netTcpBinding> </bindings>
安全性注意: 由于 Silverlight 4 不支持具有安全性的 NetTcpBinding,因此使用此绑定配置发送的任何信息都未加密,并且可被第三方截获和修改。不要使用此绑定配置发送敏感信息。 -
在 <services> 节中指定服务终结点。
<services> <service name="DuplexService.OrderService"> <!-- Specify the service endpoints. --> <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpBindingNoSecurity" contract="DuplexService.IDuplexService"> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services>
-
由于 Visual Studio Web 服务器不支持激活基于 TCP 的服务,该服务必须承载于 Internet Information Services (IIS) 中。
-
右击 DuplexServices 项目,然后选择“属性”。
-
单击左侧的“Web”选项卡,然后选择“使用本地 IIS Web 服务器”。(您可能需要向下滚动才能看到此选项。)此选项将会在“项目 URL”框中建议一条类似于 http://localhost/DuplexService 的路径,这是您应使用的值。
-
单击“项目 URL”框右侧的“创建虚拟目录”按钮以创建虚拟目录。
-
-
创建虚拟目录后,需要使用 IIS 管理器对该虚拟目录另外进行一些配置更改。
-
在“IIS 默认网站”中,使用绑定信息 4502:* 配置 net.tcp 绑定类型。
-
在 DuplexService 虚拟目录中,将 net.tcp 添加到已启用协议的列表中。
-
-
在 IIS 中承载该服务后,您仍需要在计算机上公开 Silverlight 4 HTTP 安全套接字策略文件;否则,所有尝试访问该服务的 Silverlight 4 客户端都将失败。
<?xml version="1.0" encoding ="utf-8"?> <access-policy> <cross-domain-access> <policy> <allow-from> <domain uri="*" /> </allow-from> <grant-to> <socket-resource port="4502" protocol="tcp" /> </grant-to> </policy> </cross-domain-access> </access-policy>
注意: 不支持端口 943 上基于 TCP 的套接字策略检索。使用 NetTcp 绑定时,无法切换到基于 TCP 的套接字策略检索。 有关更多信息,请参见 Silverlight 中的网络安全访问限制。
-
现在已准备好,可以在 Visual Studio 中测试双工服务了。在“解决方案资源管理器”中选择并右击 Service1.svc 文件,然后选择“在浏览器中查看”(或按 Ctrl + F5)以显示该服务的测试页。您应会看到 OrderService 服务测试页,这样便可确定该服务可用。
现在,双工服务已可供使用。如何:访问双工服务概述了下一个过程,该过程为其构建一个 Silverlight 4 客户端。
示例
Service1.svc.cs 文件应包含下列内容。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading;
namespace DuplexService
{
public class OrderService : IDuplexService
{
IDuplexClient client;
string orderName;
int orderQuantity;
public void Order(string name, int quantity)
{
// Grab the client callback channel.
client = OperationContext.Current.GetCallbackChannel<IDuplexClient>();
orderName = name;
orderQuantity = quantity;
// Pretend the service is processing and will call the client
// back in 5 seconds.
using (Timer timer = new Timer(new TimerCallback(CallClient), null, 5000, 5000))
{
Thread.Sleep(11000);
}
}
bool processed = false;
private void CallClient(object o)
{
Order order = new Order();
order.Payload = new List<string>();
// Process the order.
if (processed)
{
// Pretend the service is fulfilling the order.
while (orderQuantity-- > 0)
{
order.Payload.Add(orderName + orderQuantity);
}
order.Status = OrderStatus.Completed;
}
else
{
// Pretend the service is processing the payment.
order.Status = OrderStatus.Processing;
processed = true;
}
// Call the client back.
client.Receive(order);
}
}
public class Order
{
public OrderStatus Status { get; set; }
public List<string> Payload { get; set; }
}
public enum OrderStatus
{
Processing,
Completed
}
}