读书笔记摘要- 通道模型和绑定
1.通道模型概述:
通道栈:WCF为消息的发送和接收简历的消息管道。 通道栈的每个组件都有机会对消息进行处理,整个通道栈可编辑、可插入。确保了灵活性。
通道模型和上层才行隔离完全隔离,任何一个服务和客户端都可配置到不同的通道模型上。通道模型可分上下二部分:上面的成为协议通道,下面的成为传输通道。一个通道栈可有多个下一通道,但一般只有一个传输通道,传输通道负责吧消息进行编码并发送到远端,编码时传输通道需要使用通道栈的编码器如下图:
一般情况,协议通道负责为何消息的非业务逻辑功能,包括事务、日子、可靠消息和安全性等、
在一个通道中必须包含至少一个传输通道和编码器,传输通道负责消息的编码和发送。具体的,传输通道会尝试懂BindingContext对象中查找比编码器。
如果找不到则使用默认的编码器。不同的传输通道将使用不同的传输协议如:HTTP、TCP、IPC等。
2.消息交换模式和通道形状
消息传输层面:6种消息交互模式:
第一、数据报模式
第二、请求-响应模式
第三、双工模式
第四、会话数据报模式
第五、会话请求-响应模式
第六、会话双工模式
一个通道可以支持一种或者几种交互模式,通道通过通道形状(channel shape)来实现具体的交互模式。
通道形状值得是定义了发送、接受消息动作的10个接口,他们均继承自IChannel接口。
这10个接口分别是:IInputChannel、IOutputChannel、IRequestChannel、IreplyChannel、IDuplexChannel、IInputSessionChannel、IOutputSessionChannel、IRequestSessionChannel、IReplySessionChannel、IDuplexSessionChannel.
3、数据报模式:发送端负责把消息发送给对方并且收到确认消息后就完成消息交互的模式。
此模式下发送方唯一能确定的是消息发送成功。对应消息是否最终到达终结点、是否被成功除了、返回结果如何都不知道。示意图:
发送端代码示例:
public class Output { static void Main(string[] args) { //建立自定义通道栈 BindingElement[] bindingElements = new BindingElement[3]; bindingElements[0] = new TextMessageEncodingBindingElement();//文本编码 // bindingElements[1] = new OneWayBindingElement();//OneWayBindingElement可使得传输通道支持数据报模式 bindingElements[3] = new HttpTransportBindingElement();//Http传输 CustomBinding binding = new CustomBinding(bindingElements); //创建消息 using (Message message=new Message.CreateMessage(binding.MessageVersion,"sendMessage","Message Body")) { //创建ChannelFactory IChannelFactory<IOutputChannel> factory = binding.BuildChannelFactory<IOutputChannel>(new BindingParameterCollection()); //打开ChannelFactory factory.Open(); //创建IOutputChannel IOutputChannel outputChannel = factory.CreateChannel(new EndpointAddress("http://localhost:9090.InputService")); //打开IOutputChannel outputChannel.Open(); //发送信息 outputChannel.Send(message); Console.WriteLine("已成功发送信息"); //关闭IOutputChannel outputChannel.Close(); //关闭ChannelFactory factory.Close(); } } }
接收端代码示例:
public class Input { static void Main(string[] args) { //建立和发送端相同的通道栈 BindingElement[] bindingElements = new BindingElement[3]; bindingElements[0] = new TextMessageEncodingBindingElement();//文本编码 //bindingElements[1] = new OneWayBindingElement();//OneWayBindingElement可使得传输通道支持数据报模式 bindingElements[2] = new HttpTransportBindingElement();//Http传输 //创建自定义绑定 CustomBinding binding = new CustomBinding(bindingElements); //建立ChannelListner IChannelListener<IInputChannel> listener = binding.BuildChannelListener<IInputChannel>(new Uri("http://localhost:9090/InputService"),new BindingParameterCollection()); listener.Open();//打开ChannelListner //创建IInputChannel IInputChannel inputChannel = listener.AcceptChannel(); inputChannel.Open();//打开IInputChannel Console.WriteLine("开始接收信息"); Message message = inputChannel.Receive();//接收并打印信息 Console.WriteLine("接收到一条消息,action为:{0},body为{1}",message.Headers.Action,message.GetBody<string>()); message.Close();//关闭消息 inputChannel.Close();//关闭通道 listener.Close();//关闭监听器 Console.Read(); } }
4.请求-响应模式:客户端发送一个消息并且接受返回消息完成一次交互。特殊的双工模式。该模式消息必须是客户端发起,并且从服务器端返回的只有一条消息,客户端在发送出消息后会阻止当前线程并且等待服务端返回消息,很适用于HTTP协议。如图交互模式:
为实现请求-响应模式,客户端需要实现IRequestChannel接口,服务端需要实现IReplyChannel接口,代码示例:
发送端:
public class Output { static void Main(string[] args) { BindingElement[] bindingElements = new BindingElement[2]; bindingElements[0] = new TextMessageEncodingBindingElement();//文本编码 bindingElements[1] = new HttpTransportBindingElement();//Http传输 //自定义绑定 CustomBinding binding = new CustomBinding(bindingElements); using (Message message= Message.CreateMessage(binding.MessageVersion,"sendMessage","Message Body")) { //创建ChannelFactory IChannelFactory<IRequestChannel> factory = binding.BuildChannelFactory<IRequestChannel>(new BindingParameterCollection()); factory.Open();//打开ChannelFactory //创建IRequestChannnel IRequestChannel requestChannel = factory.CreateChannel(new EndpointAddress("http://localhost:9090/RequestReplayService")); requestChannel.Open();//打开通道 Message response = requestChannel.Request(message); //发送消息 Console.WriteLine("已成功发送消息"); //查看返回的消息 Console.WriteLine("接收到一套返回消息,action为:{0},body为:{1}",response.Headers.Action,response.GetBody<string>()); requestChannel.Close();//关闭通道 factory.Close();//关闭工厂 } } }
接收端:
public class Input { static void Main(string[] args) { //建立和发送端相同的通道栈 BindingElement[] bindingElements = new BindingElement[2]; bindingElements[0] =new TextMessageEncodingBindingElement();//文本编码 bindingElements[1] = new HttpTransportBindingElement();//Http传输 //自定义绑定 CustomBinding binding = new CustomBinding(bindingElements); //建立ChannelListner IChannelListener<IReplyChannel> listener = binding.BuildChannelListener<IReplyChannel>(new Uri("http://localhost:9090/RequestReplyService"),new BindingParameterCollection()); listener.Open();//打开监听 //创建IReplyChannel IReplyChannel replyChannel = listener.AcceptChannel(); replyChannel.Open();//打开通道 Console.WriteLine("开始接收消息。。。。"); RequestContext requestContext = replyChannel.ReceiveRequest(); Console.WriteLine("接收到一条消息,action为:{0},body为:{1}", requestContext.RequestMessage.Headers.Action, requestContext.RequestMessage.GetBody<string>()); //创建返回消息 Message response = Message.CreateMessage(binding.MessageVersion,"response","response body"); //发送返回消息 requestContext.Reply(response); requestContext.Close(); replyChannel.Close();//关闭通道 listener.Close();//关闭监听 } }
5、双工模式:客户端和服务器端都可以任意的向对方发送消息。此刻发送端和接收端是相对的。
为了实现双工模式。通信端需要实现IDuplexChannel接口,IDuplexChannel接口同时实现了IInputChannel接口和IOutputChannel接口。使用其来实现接收和发送信息。
IInputChannel负责接收信息,IOutputChannel接口负责发送信息。发送端代码示例:
public class DuplexSender { static void Main(string[] args) { NetTcpBinding binding = new NetTcpBinding();//创建绑定 using (Message message=Message.CreateMessage(binding.MessageVersion,"sendMessage","Message Body")) { //创建ChannelFactory IChannelFactory<IDuplexChannel> factory = binding.BuildChannelFactory<IDuplexChannel>(new BindingParameterCollection()); factory.Open();//打开通道工厂 //创建IRequestChannel IDuplexChannel duplexChannel = factory.CreateChannel(new EndpointAddress("net.tcp://localhost:9090/DuplexService/Point2")); duplexChannel.Open();//打开通道 duplexChannel.Send(message);//发送信息 Console.WriteLine("已经成功发送信息。"); duplexChannel.Close();//关闭通道 factory.Close();//关闭通道工厂 } } }
接收端大体类似。此处省略。
6、带会话的数据报模式、请求-响应模式和双工模式
从通信的层面说会话的概率类似网络协议中链接的概念,带会话的通信模式类似面向链接的网络协议,不带会话的通信模型相当于是无连接的网络协议,
典型例子是TCP协议和UDP协议。
带会话通信模型中使用的通道形状是IInputSessionChannel、IOutputSessionChannel、IRequestSessionChannel、IReplySessionChannel和IDuplexSessionChannel。
在通道模型中,每个逻辑会话都表现为一个会话通道的实例。所以,由客户端创建并在服务器接收的每个新会话都与每一端的一个新会话相对应。
带会话模式和不带会话模式的区别如图所示:
使用HTTP协议进行消息传输时候,受到协议的限制,数据报模式和双工模式并不能被使用,需要改变通道的形状来解决,即在传输通道上添加特定的协议通道,
强制使用某种传输通道不支持的传输模式。如下列代码:
//建立自定义通道栈 BindingElement[] bindingElements = new BindingElement[3]; bindingElements[0] = new TextMessageEncodingBindingElement();//文本编码 bindingElements[1] = new OneWayBindingElement();//OneWayBindingElement可使得传输通道支持数据报模式 bindingElements[3] = new HttpTransportBindingElement();//Http传输
OneWayBindingElement协议通道就行通道形状改变,在WCF中。一共有两种协议通道类型用以通道形状改变,分别是:OneWayBindingElement和CompositeDuplexBindingElement.
OneWayBindingElement把通道形状改为数据报模式、
CompositeDuplexBindingElement把通道形状改变为双工模式
7、通道形状和上层服务协议
备注:不是所有通道都会试下所以通道形状,有时被迫用其他通道形状。如用IDuplexChannel或IRequestChannel/IReplyChannel来代替。
8、通道管理器
在WCF中实现了二类通道管理器即:IChannelListener<T>和IChannelFactory<T>接口
IChannelListener<T>:负责接收端的消息交互控制,负责侦听消息、建立通道栈并提供通道栈顶层通道的引用。可直接使用ServiceHost类型。在
ServiceHost内部使用了IChannelListener<T>来实现通道的管理。示例代码:
//建立ChannelListner IChannelListener<IReplyChannel> listener = binding.BuildChannelListener<IReplyChannel>(new Uri("http://localhost:9090/RequestReplyService"),new BindingParameterCollection()); listener.Open();//打开监听 //创建IReplyChannel IReplyChannel replyChannel = listener.AcceptChannel(); replyChannel.Open();//打开通道
IChannelFactory<T>:负责发送端控制消息的发送。负责创建并管理通道栈。可用ClentBase<T>代替IChannelFactory<T>。
//创建ChannelFactory IChannelFactory<IRequestChannel> factory = binding.BuildChannelFactory<IRequestChannel>(new BindingParameterCollection()); factory.Open();//打开ChannelFactory //创建IRequestChannnel IRequestChannel requestChannel = factory.CreateChannel(new EndpointAddress("http://localhost:9090/RequestReplayService")); requestChannel.Open();//打开通道
IChannelFactory<T>负责关闭通道栈。而IChannelListener<T>不需要。IChannelListener<T>可独立他的通道栈而关闭
9、ICommunicationObject接口和状态改变
定义状态机的统一模型,基本是由的通信组件包括通道和通道管理器都实现了ICommunicationObject接口,定义如下:
public interface ICommunicationObject { //属性状态 CommunicationState State { get; } //事件 event EventHandler Opening; event EventHandler Faulted; event EventHandler Closing; event EventHandler Closed; event EventHandler Opened; // 改变状态机的方法 void Abort(); IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state); IAsyncResult BeginClose(AsyncCallback callback, object state); IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state); IAsyncResult BeginOpen(AsyncCallback callback, object state); void Close(); void Close(TimeSpan timeout); void EndClose(IAsyncResult result); void EndOpen(IAsyncResult result); void Open(); void Open(TimeSpan timeout); }
以上代码可见主要集中在状态机的管理所包括改变状态的方法、状态改变吃饭的时间以及状态的获取。共六种状态如下枚举定义:
public enum CommunicationState { Created = 0, Opening = 1, Opened = 2, Closing = 3, Closed = 4, Faulted = 5 }
状态机的状态变化图:不可逆。
关于灵活运用这些事件可以在状态改变时执行特定的状态,状态事件示例:
[ServiceContract] public interface Iservice { [OperationContract] string HelloWorld(string name);//服务操作 } public class HelloWorldProxy : ClientBase<Iservice>, Iservice //客户端代理类型 { //定义绑定 public static readonly Binding HelloWorldBinding= new NetNamedPipeBinding(); public static readonly EndpointAddress HelloWorldAddress = new EndpointAddress(new Uri("net.pipe://localhost/HelloWorld")); public HelloWorldProxy() : base(HelloWorldBinding,HelloWorldAddress) { //构造方法 } public string HelloWorld(string name) { return Channel.HelloWorld(name);//使用Channel属性对服务进行调用 } } public class Client { static void Main(string[] agrs) { using (HelloWorldProxy proxy=new HelloWorldProxy()) { ICommunicationObject communicationObject=(ICommunicationObject)proxy; communicationObject.Opening+=new EventHandler(Opened); communicationObject.Closed += new EventHandler(Closed); //利用代理调用服务 Console.WriteLine(proxy.HelloWorld("WCF")); } } //Opened事件 static void Opened(object sender,EventArgs args) { Console.WriteLine("状态机开启!"); } //Closed事件 static void Closed(object sender,EventArgs args) { Console.WriteLine("状态机关闭!"); } }