本主题演示了创建使用单向协定的方法所需的基本步骤。这些方法从客户端调用 Windows Communication Foundation (WCF) 服务上的操作但不期待答复。例如,可以使用这种类型的协定将通知发布给许多订户。在创建双工(双向)协定(可使得客户端和服务器可以独立地相互通信,这样双方都可以启动对另一方的呼叫)时,还可以使用单向协定。具体而言,这样做可允许服务器对客户端进行单向呼叫,而客户端可以将这些呼叫视为事件。有关指定单向方法的详细信息,请参见 IsOneWay 属性和 OperationContractAttribute 类。
有关 创建用于双工协定的客户端应用程序的更多信息,请参见如何:使用单向和请求-答复协定访问 WCF 服务。有关可运行的示例,请参见Service Contract: One-Way示例。
创建单向协定
-
通过将 ServiceContractAttribute 类应用到定义服务将要实现的方法的接口,创建服务协定。
-
通过将 OperationContractAttribute 类应用到相应的方法,指示客户端可以调用接口中的哪些方法。
-
通过将 IsOneWay 属性设置为 true,可将不得具有输出(没有返回值且没有 out 参数或 ref 参数)的操作指定为单向操作。注意,默认情况下,使用 OperationContractAttribute 类的操作都满足请求-答复协定,原因是默认情况下 IsOneWay 属性为 false。因此,如果需要对方法使用单向协定,则必须将 attribute 属性的值显式指定为 true。
此示例演示具有单向服务操作的服务协定。客户端不会像在双向服务操作中那样等待服务操作完成。此示例基于入门示例并使用 wsHttpBinding 绑定。此示例中的服务是自承载控制台应用程序,通过它可以观察接收和处理请求的服务。客户端也是一个控制台应用程序。
注意: |
---|
本主题的末尾介绍了此示例的设置过程和生成说明。 |
若要创建单向服务协定,请定义服务协定,将 OperationContractAttribute 类应用于每个操作,并将 IsOneWay 设置为 true,如下面的示例代码所示:
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")] public interface IOneWayCalculator { [OperationContract(IsOneWay=true)] void Add(double n1, double n2); [OperationContract(IsOneWay = true)] void Subtract(double n1, double n2); [OperationContract(IsOneWay = true)] void Multiply(double n1, double n2); [OperationContract(IsOneWay = true)] void Divide(double n1, double n2); } |
为了演示客户端不会等待服务操作完成,此示例中的服务代码实现了五秒钟的延迟,如下面的示例代码所示:
/ This service class implements the service contract. // This code writes output to the console window. [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)] public class CalculatorService : IOneWayCalculator { public void Add(double n1, double n2) { Console.WriteLine("Received Add({0},{1}) - sleeping", n1, n2); System.Threading.Thread.Sleep(1000 * 5); double result = n1 + n2; Console.WriteLine("Processing Add({0},{1}) - result: {2}", n1, n2, result); } ... } |
当客户端调用服务时,调用不等待服务操作完成即返回。
运行示例时,客户端和服务活动将显示在服务和客户端控制台窗口中。您可以看到服务从客户端接收消息。在每个控制台窗口中按 Enter 可以同时关闭服务和客户端。
客户端在服务之前完成,说明了客户端没有等待单向服务操作完成。客户端输出如下所示:
Add(100,15.99) Subtract(145,76.54) Multiply(9,81.25) Divide(22,7) Press <ENTER> to terminate client. |
服务输出如下所示:
The service is ready. Press <ENTER> to terminate service. Received Add(100,15.99) - sleeping Received Subtract(145,76.54) - sleeping Received Multiply(9,81.25) - sleeping Received Divide(22,7) - sleeping Processing Add(100,15.99) - result: 115.99 Processing Subtract(145,76.54) - result: 68.46 Processing Multiply(9,81.25) - result: 731.25 Processing Divide(22,7) - result: 3.14285714285714 |
注意: |
---|
HTTP 从定义上讲是一个请求/响应协议;当发出请求时,即返回响应。即使对于通过 HTTP 公开的单向服务操作,也是如此。当调用操作时,服务在执行服务操作之前返回 HTTP 状态码 202。此状态码表示请求已被接受进行处理,但处理尚未完成。调用操作的客户端在从服务收到 202 响应之前处于阻止状态。当使用绑定(配置为使用会话)发送多个单向消息时,这可能会产生某些意外行为。此示例中使用的 wsHttpBinding 绑定配置为默认使用会话来建立安全上下文。默认情况下,会话中的消息一定会按照它们的发送顺序到达。因此,当发送会话中的第二个消息时,在处理完第一个消息之前不会处理第二个消息。这样的结果是,在处理完上一个消息之前,客户端不会收到消息的 202 响应。因此,客户端似乎是阻止了每个后续的操作调用。为了避免此行为,此示例对运行库进行了配置,以便将消息并发调度给不同的实例进行处理。此示例将 InstanceContextMode 设置为 PerCall,使每个消息可以由不同的实例来处理。ConcurrencyMode 设置为 Multiple,以允许多个线程同时调度消息。 |