学习WCF之路8:消息通信模式


WCF的服务端与客户端在通信时有三种模式:单向模式、请求/应答模式和双工模式。


如果选用了单向模式,调用方在向被调用方进行了调用后不期待任何回应,被调用方在执行完调用后不给调用方任何反馈。如客户端通过单向模式调用了一个服务端的操作后,就去干别的了,不会等待服务端给他任何响应,他也无从得知调用是否成功,甚至连发生了错误也全然不知。这种模式的特点是,客户端在调用操作后立即返回,从客户端角度看,用户操作的响应是非常快的,只是客户端无法得知调用结果。


如果选用了请求/应答模式,客户端向服务端发出调用后会一直等待服务端的回复,服务端在执行完操作后会把结果返回给客户端,即使服务操作签名返回值为void,服务端还是会返回一条空消息,告诉客户端调用完成了,客户端在接到返回后才会从调用方法返回继续进行下面的工作。这种模式的特点是客户端总是可以知道服务执行的情况,如果出错,错误也会返回,客户端对服务的执行监控的很好,但是由于在服务返回之前客户端会一直等待,所以如果服务端的服务执行时间比较长的话,客户端这边的用户响应就会很慢,如果客户端对服务的调用与用户界面在同一线程,在用户看来,应用程序就死在那里了。


如果选用了双工模式,客户端和服务端都可以单独的向对方发送消息调用,其实这种模式是在单向模式基础上进行的,两边的调用都是单向调用,但是两边都可以独立的进行,谁也不用等待谁,这种模式比较复杂一些。


(1)单向模式与请求/应答模式

这两个模式的设置位置相同,都是通过修改操作协定的OperationContract属性的IsOneWay属性来设置。

①单向模式配置:

[ServiceContract]
    public interface IHelloWCF
    {
        [OperationContract(IsOneWay=true)]
        void PHelloWCF();
    }  
注意,在单向模式下,返回值必须是void,并且不能使用任何Out或Ref的方式返回参数值,也就是说不能以任何手段返回任何值,这是基础结构所不允许的,这样做会导致服务端抛出异常。而在请求/应答模式下,这些都是可以的,即使没有返回值(返回值为void),返回消息也会照样发送,只不过是个空消息。

②请求/应答模式配置:

[ServiceContract]
    public interface IHelloWCF
    {
        [OperationContract(IsOneWay=false)]
        string PHelloWCF();
    }  

如果不配置IsOneWay属性,那么他默认是False的,也就是说默认的消息通信模式是请求/应答模式,除非我们显式的指定为单向模式。


实现的小例子:

①单向模式:

服务端.cs文件源代码:

using System;
using System.ServiceModel;

namespace LearnWCF
{
	public interface IHelloWCF
    {
        [OperationContract(IsOneWay=true)]
        void PHelloWCF();
    }

	public class HelloWCF:IHelloWCF
    {
		public void PHelloWCF()
        {
            System.Threading.Thread.Sleep(3000);
        }
    }
}
服务端配置文件源代码:

<configuration>
  <system.serviceModel>
    <services>
      <service name="LearnWCF.HelloWCF" behaviorConfiguration="metadataExchange">
        <endpoint address="" binding="wsHttpBinding" contract="LearnWCF.IHelloWCF"/>
        <endpoint address="mex" binding="wsHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="metadataExchange">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>
服务端.svc文件源代码:

<%@ServiceHost
			 language="c#"
			 Debug="true"
			 Service="LearnWCF.HelloWCF"
			 %>

客户端调用源代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.ServiceModel;

namespace SessionMode_client
{
    class Program
    {
        static void Main(string[] args)
        {
            SessionMode.HelloWCFClient client = new SessionMode.HelloWCFClient();
            Console.WriteLine(DateTime.Now.ToLongTimeString());
            client.PHelloWCF();
            Console.WriteLine(DateTime.Now.ToLongTimeString());
            Console.ReadLine();
        }
    }
}

这两部分的代码应该是非常熟悉了,唯一的区别是,单向模式返回值必须是void,所以服务实现方法只是延时3秒。

保存运行结果如下:



由结果可以看到,当处于单向模式时,只用了1秒,客户端与服务端建立会话后把调用送出就立即返回了,没有等待服务端睡的那三秒,当然此时的客户端也根本就不知道服务端在做什么。


②请求/应答模式

这里只需将IsOneWay属性改成false即可,保存运行结果如下;


由结果可以看到,在请求/应答模式下,整个调用花费了4秒钟,除了服务方法中Sleep了3秒,建立会话通讯什么的还用了1秒,在服务端方法Sleep的时候,客户端一直在等待。


注意事项:请求应答模式是需要会话支持的,必须使用支持会话的绑定,而且服务协定的SessionMode必须至少为Allowed,服务类的ServiceBehavior的InstanceContextMode必须是PerSession,我们在这里没有配置,因为他们是默认的,但是我们必须知道他们需要这样的配置才能支持请求/应答模式。


出现的问题:如果你在试验中遇到了莫名其妙的问题,尝试把客户端服务引用全部删掉重新添加服务引用,因为有的时候更新服务引用不总是那么好用。



(2)双工模式

       在双工模式下,服务端和客户端都可以互相调用对方,并且每次调用都是单向模式,不需要等待。这样调用双方就会有很好的异步体验,想调的时候就调,然后我就去干别的,什么时候调用完成了,你可以通过回调来通知我,我再决定下一步的动作,谁都不等谁(一般调用是客户端发起的,回调是由服务端发起的)。

实现双工模式的条件:①必须支持使用双工通信的绑定。(例如wsDualHttpBinding)

                                    ②必须使用会话。

       客户端要想调服务端,客户端必须拥有服务协定接口,而服务端必须拥有服务协定接口以及实现接口的服务类,在运行时服务端还要有服务类的实例。同理服务端调用客户端也是需要这些因素,只是名称变为回调接口,回调类。

双工模式运行的小例子(还是挂载在IIS上):

①首先建立SVC文件

<%@ServiceHost
			 language="c#"
			 Debug="true"
			 Service="LearnWCF.HelloWCF"
			 %>
这与之前的一致。

②编写服务代码文件

using System;
using System.ServiceModel;


namespace LearnWCF
{
    [ServiceContract(SessionMode=SessionMode.Required,
        CallbackContract=typeof(IHelloWCFCallback))]
    public interface IHelloWCF
    {
        [OperationContract(IsOneWay=true)]
        void PHelloWCF();
    }


    //定义回调协定接口
    public interface IHelloWCFCallback
    {
        [OperationContract(IsOneWay = true)]
        void Callback(string msg);
    }


    public class HelloWCF:IHelloWCF
    {
        private int _Counter;
        public void PHelloWCF()
        {
            System.Threading.Thread.Sleep(10000);
            string msg = "Hello from service!Time:" + DateTime.Now.ToLongTimeString();
            //获得回调通道
            IHelloWCFCallback callbackChannel = OperationContext.Current.GetCallbackChannel<IHelloWCFCallback>();
            //调用回调操作
            callbackChannel.Callback(msg);
        }
    }
}


首先,将服务接口的SessionMode属性设置成Required,必须使用会话。

接着,定义了一个IHelloWCFCallback的回调协定接口,这个接口的实现是写在客户端的。

然后,指定了IHelloWCF服务协定接口的回调协定为该回调协定接口,这样服务协定就知道怎样进行回调了。

最后,是实现服务接口的实现,实现过程是:第一步是休眠5秒,第二部是获得回调通道,第三步是调用回调操作,              将字符串msg传递给回调函数。


③编写配置文件

<configuration>
  <system.serviceModel>
    <services>
      <service name="LearnWCF.HelloWCF" behaviorConfiguration="metadataExchange">
        <endpoint address="" binding="wsDualHttpBinding" contract="LearnWCF.IHelloWCF"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="metadataExchange">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

这里主要修改的地方为,将服务终结点的绑定方式修改成wsDualHttpBinding,从而支持双工通信。


④建立客户端应用程序。

第一步,创建控制台应用程序,并添加服务引用。

第二部,编写客户端代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


using System.ServiceModel;


namespace SessionMode_client
{
    class Program
    {
        static void Main(string[] args)
        {
            //建立回调服务对象
            HelloWCFCallback callbackObject = new HelloWCFCallback();
            //建立实例上下文对象
            InstanceContext clientContext = new InstanceContext(callbackObject);
            //初始化代理类对象
            SessionMode.HelloWCFClient client = new SessionMode.HelloWCFClient(clientContext);
            Console.WriteLine("Client call begin:"+DateTime.Now.ToLongTimeString());
            client.PHelloWCF();
            Console.WriteLine("Client call end:"+DateTime.Now.ToLongTimeString());
            Console.WriteLine("Client can process other things.");
            Console.ReadLine();
        }
    }


    public class HelloWCFCallback : SessionMode.IHelloWCFCallback
    {
        public void Callback(string msg)
        {
            Console.WriteLine(msg);
        }
    }
}
首先,是建立回调的服务对象。

然后,是将得到的服务对象作为参数,建立实例上下文对象。

接着,是将得到的实例上下文对象作为代理类的构造函数参数,来初始化代理类,代理类会检测到服务端元数据中服务协定使用双工,然后会为我们准备好双工通道。

接着,使用代理类对象来进行调用操作。

最后,是实现回调协定接口,这里就是把服务端传过来的参数输出。


⑤最后,保存运行,得到的结果:


通过结果以及结合源代码,可以知道:客户端就调用了服务端一次,就花费了2s,然后客户端就去干别的事去了;客户端调用服务端,并休眠5s后,服务端执行回调操作,最后一行结果是服务端主动回调客户端方法后输出的结果。


双工通信小结:

关键点:

(1) 通信两端都有两个协定接口,接口的实现在两边一边一个。

(2) 支持双工的绑定。

(3) 指定服务协定的回调协定以建立回调联系。

(4) 客户端自己构造运行时服务类对象和实例上下文对象。

(5) 服务端通过操作上下文获得回调通道 。

(6) 需要会话的支持。

(7) 两个协定中的协定操作应为单向模式。


应用过程中发现一个问题:

在服务端,若服务名为WCFService,在定义回调函数接口时取名为ICallback;然后在客户端引用服务时,发现代理类已经自动生成回调接口,但是该接口名与服务端不一致,取名为IWCFServiceCallback,但是用法一致。目前不了解原因,找资料也找不到,因为不影响使用,先搁置。

服务器端的定义:


客户端的引用:








  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值