信道及信道栈
前面已经提及过,WCF中客户端与服务端的交互都是通过消息来进行的。消息从客户端传送到服务端会经过多个处理动作,在WCF编程模型中,这些动作是按层次进行的:上一层次对消息处理完成后,将消息传递给下一层次,依次类推。这里对应的每个层即为信道(我们可以理解为消息通道)。而这一些列的信道层构成了一个信道栈。明显的,信道栈中的最后一个信道应该是传输信道,该信道将处理后的消息发送给服务端或接受从服务端返回的消息应答,而在传输信道之上还有一系列的协议信道,这些信道将对消息进行加工处理。
我们可以把信道看做是绑定的实现,从前面示例可以看出,绑定实际上是一个终结点的配置,这个配置影响到客户端与服务端交互时消息的传输及处理方式(使用何种协议进行传输、消息的编码方式、消息遵循的安全协议等待)。绑定本身是有一系列的绑定元素组合而成的,每个绑定元素必然会有一个信道层与之对应,最终绑定元素通过对应的信道层对消息产生影响。
信道工厂
信道堆栈通常是使用工厂模式创建的,在这种模式中,绑定创建信道堆栈。 在客户端,使用绑定生成信道工厂ChannelFactory,然后ChannelFactory生成信道堆栈并返回对堆栈中顶部信道的引用。 之后,应用程序可以使用此信道发送消息。
在服务端,使用绑定生成信道侦听器 IChannelListener,用于侦听传入消息。 IChannelListener 通过创建信道堆栈并将应用程序引用传递给顶部信道,将消息提供给侦听应用程序。 之后,应用程序使用此信道接收传入消息。(注意:关于服务端使用的信道侦听器将在后面介绍,本文只讲述客户端使用的信道工厂)
OK,总结一下:
1.绑定有一些列的绑定元素组成,每个绑定元素对应一个信道
2.信道负责对消息的加工处理
3.信道按绑定元素在绑定中的堆叠顺序组成信道栈
4.信道由信道工厂生成,而信道工厂由绑定来创建(绑定调用每个绑定元素的信道工厂创建信道,并返回信道栈中的首个信道)
ChannelFactory<T>泛型类
信道工厂类是一个泛型类,用以创建信道。参数类型T通常是我们的服务契约接口(IFirstService),这样既可实现信道与服务契约的关联。
信道工厂有7个公共的构造函数,构造函数的参数最终是为了构建一个服务终结点(ServiceEndPoint),如果使用无参数的构造函数,则实例化后可以通过EndPoint属性来设置终结点的属性。关于具体函数声明,请参见MSDN。
信道工厂另一个重要的方法是创建信道:CreateChannel,此方法返回一个T类型实例,即服务契约接口的实例。所以通过这个实例,在客户端我们可以像操作本地对象一样,直接调用服务方法。
在示例一中,我们是使用的svcutil生成的客户端代码,在生成的代码文件中,定义了一个服务契约接口IFirstService,当然这个接口是必须的。而另一个类为FirstServiceClient,它继承与ClientBase<IFirstService>及IFirstService,在此类中封装了对信道工厂的操作,我们通常把此类称作客户端代理类,注意类中IFirstService接口方法的实现代码:
-
public string GetData(string name)
-
{
-
return base.Channel.GetData(name);
-
}
直接调用了基类(ClientBase<IFirstService>中Channel的GetData方法,Channel属性实际上就是通过ChannelFactory<IFirstService>信道工厂的CreateChannel方法返回的一个IFirstService实例。所有,FirstServiceClient类只是为了封装信道工厂的基本操作,svcutil通过生成此类,是我们在客户端应用程序中无需直接操作信道工厂,而直接调用服务,回忆我们客户端Program.cs中的调用代码:
-
FirstServiceClient client = new FirstServiceClient("BasicHttpBinding_IFirstService");
-
Console.WriteLine("使用BASIC HTTP绑定:" + client.GetData(key));
关于ClientBase<IFirstService>类是WCF为客户端提供的一个基类,其中实现了对信道工厂的操作。不过此类只能用作基类并且不可实例化,所以客户端代理类通过继承ClientBase<IFirstService>的方式,实现了对信道的基本操作。了解客户端代理类,服务契约接口及ClientBase类的关系后,我们完全可以自己实现客户端代理类:从ClientBase派生,并通过ClientBase中的Channel对应的服务契约方法来实现客户端代理类的服务契约接口(GetData方法的实现)。
当然,通过客户端代理类方法是一种标准的客户端实现方式,但是我们也可以通过直接操作信道工厂来实现客户端调用代码,其实很简单:
-
ChannelFactory<IFirstService> cf = new ChannelFactory<IFirstService>(new BasicHttpBinding(), "http://localhost:8000/");
-
IFirstService s = cf.CreateChannel();
-
Console.WriteLine(s.GetData(key));