《WCF技术内幕》翻译34:第2部分_第6章_通道:通道接口和基本类型

本节目录:
 

通道接口和基本类型

本章开始部分曾经提到过,学习WCF通道基础结构的一个关键部分就是了解WCF系统在通道层使用的接口和类型。本节系统整理了这些复杂的类型系统,深入浅出地讲述各个接口和类型的原理,使得读者可以更容易掌握这些知识点。

IChannel接口

System.ServiceModel.Channels.IChannel接口看似简单,但是它对于通道层的实现至关重要。所有的通道和通道工厂必须实现它。换句话说,一个集成了CommunicationObject的类型通常也会实现IChannel接口。在详细学习IChannel接口的作用以前,我们先来看看它的基本结构:
 
 
 
 
InBlock.gif public interface IChannel : ICommunicationObject {
InBlock.gif            T GetProperty<T>() where T: class;
InBlock.gif}
你或许会问自己:“为什么会这么重要呢?”记得 CommunicationObject堆栈里的每个CommunicationObject对象都有一些特定的功能,并且只有栈顶的通道才可以被调用者调用。当堆栈组合正常的情况啊,GetProperty<T>方法提供了在CommunicationObject堆栈里查询特定功能的途径。例如,你也许想知道CommunicationObject堆栈是否支持特定的通道外形,MessageVersion或安全功能。下面代码演示了调用如何使用IChannel.GetProperty<T>方法:
// assume channel stack (myChannelStack) created假定通道堆栈已经创建完毕
InBlock.gifMessageVersion messageVersion =
InBlock.gif myChannelStack.GetProperty<MessageVersion>();
InBlock.gif if(MessageVersion != null){
InBlock.gif // do something
InBlock.gif}
InBlock.gif // app continues
和 CommunicationObject堆栈里的其它成员一样,当一个通道不知道如何响应查询的时候,它会使用委托去调用堆栈里的下一个通道。GetProperty<T>的简单实现如下:
InBlock.gif public override T GetProperty<T>() where T: class {
InBlock.gif             if ( typeof(T) == typeof(MessageVersion)) {
InBlock.gif                         // this type knows only how to return MessageVersion
InBlock.gif                         return (T) this.MessageVersion;
InBlock.gif         }
InBlock.gif             // no other capabalities are known here, so
InBlock.gif             // delegate the query to the next node
InBlock.gif             return this.inner.GetProperty<T>();
InBlock.gif}
如上所示,这个 GetProperty<T>方法的实现可以只返回MessageVersion,并且它的可以调用查询堆栈里的下一个通道的功能。如果查询的功能不存在,就会返回null,而不是抛出异常。因为使用了委托来嵌套查询,所以只有最底层的通道查询方法才会抛出null。

数据报通道:IInputChannel与IOutputChannel

第三张里曾经提到,数据报消息交换模式非常强大而且极具可伸缩性。在数据报消息交换模式里,发送者发送一个消息到接收者,而不期望得到回复。更简单地说,发送者输出(发送)一个消息,接收者接受一个消息作为输入。因此, WCF基础结构定义了数据报交换模式里的发送者接口名为System.ServiceModel.Channels.IOutputChannel,而接受者的接口名为System.ServiceModel.IInputChannel。
发送接口:IOutputChannel
Like its role in the Datagram MEP, the IOutputChannel interface is simple, as shown here:
和其在数据报交换模式里角色一样,IOutputChannel接口比较简单,如下所示:
InBlock.gif public interface IOutputChannel : IChannel, ICommunicationObject {
InBlock.gif            IAsyncResult BeginSend(Message message, AsyncCallback callback,
InBlock.gif                                                         Object state);
InBlock.gif            IAsyncResult BeginSend(Message message, TimeSpan timeout,
InBlock.gif                                                         AsyncCallback callback, Object state);
InBlock.gif             void EndSend(IAsyncResult result);
InBlock.gif             void Send(Message message);
InBlock.gif             void Send(Message message, TimeSpan timeout);
InBlock.gif
InBlock.gif            EndpointAddress RemoteAddress { get; }
InBlock.gif            Uri Via { get; }
InBlock.gif}
首先, IOutputChannel实现了IChannel和ICommunicationObject接口。任何实现了IOutputChannel接口的类型,都必须定义公有的通道状态机成员和GetProperty<T>方法。为了支持异步编程模型(APM),接口定义了同步和异步的Send方法。
RemoteAddress属性指的是消息发送的地址。值得注意的是,这不一定是消息发送的真实地址。回忆一下第2章“面向服务”里的邮政服务的例子,这在一个消息接收者的情况下,对于标记地址十分有用。IOutputChannel上Via属性表示的另外一个地址是消息发送的目标地址。
接收接口:IInputChannel
接收数据报消息的通道实现了IInputChannel接口。对于接收者在数据报交换模式里的角色,IInputChannel只定义了接收成员而没有发送成员。IInputChannel接口的定义如下:
InBlock.gif public interface IInputChannel : IChannel, ICommunicationObject {
InBlock.gif EndpointAddress LocalAddress { get; }
InBlock.gif
InBlock.gif // Receive Methods
InBlock.gif IAsyncResult BeginReceive(AsyncCallback callback, Object state);
InBlock.gif IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback,
InBlock.gif                                                        Object state);
InBlock.gif Message EndReceive(IAsyncResult result);
InBlock.gif Message Receive();
InBlock.gif Message Receive(TimeSpan timeout);
InBlock.gif
InBlock.gif // TryReceive Methods
InBlock.gif IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback,
InBlock.gif                                                             Object state);
InBlock.gif bool EndTryReceive(IAsyncResult result, out Message message);
InBlock.gif bool TryReceive(TimeSpan timeout, out Message message);
InBlock.gif
InBlock.gif // Waiting Methods
InBlock.gif IAsyncResult BeginWaitForMessage(TimeSpan timeout,
InBlock.gif                                                                     AsyncCallback callback,
InBlock.gif                                                                     Object state);
InBlock.gif bool EndWaitForMessage(IAsyncResult result);
InBlock.gif bool WaitForMessage(TimeSpan timeout);
InBlock.gif}
通常,接收程序会消极地等待消息到来。为此, IInputChannel定义了三个等待消息的方法。这些方法的命名没有什么规律,但是为了简便,这里就分为Receive、TryReceive和WaitForMessage几组方法。所有的方法都包含同步和异步定义。
Receive方法等待一段时间,如果消息在这段时间内到达,该方法会返回一个Message引用。如果在规定的时间内,消息还没到达,这些方法就会抛出一个TimeoutException。TryReceive方法会等待一段时间,然后通过out参数返回一个Message引用。这些方法返回一个Boolean值表示能否在期望的时间内返回Message。Receive和TryReceive方法最大的不同就是如何显示超时结果。
与Receive和TryReceive 不同,WaitForMessage方法不会返回一个Message引用,或者一个out参数。它会返回一个表示一个消息是否到达的Boolean值。这有点像I/O基础结构里的Peek功能。把WaitForMessage与Receive或TryReceive一起使用,可以实现等待一个消息并接受一个消息。
当消息要参与到一些其他的活动中的时候,WaitForMessage方法就非常有用。比如,思考以下情况,当一个Message必须参与到一个事务里。在这个例子里,对于Receive和TryReceive方法的调用必须包装到事物里。如果Message没有到达,调用者必须终止事务。如果,调用者使用了WaitForMessage方法,这次调用就没必要发生在事务的范围内。如果WaitForMessage返回false,调用者仅仅需要再调用WaitForMessage方法。一旦Message到达,调用者能够启动一个事务,然后调用Receive或TryReceive方法执行相应的任务。

请求/应答通道:IRequestChannel和IReplyChannel

在请求/应答消息交换模式里,消息的参与者都要发送和接收消息。发送者发送消息给接收者,然后等待回复。而接收者会接收请求消息,然后发送一个回复消息。为了实现通道形状,IRequestChannel和IReplyChannel接口分别定义了符合请求/应答消息交换模式的成员.
发送接口:IRequestChannel

IRequestChannel接口定义了发送请求消息和接收应答消息的相关成员。通道层里发送和接收消息的成员都包含同步和异步的定义。如下所示:
InBlock.gif public interface IRequestChannel : IChannel, ICommunicationObject {
InBlock.gif // Request Methods
InBlock.gif IAsyncResult BeginRequest(Message message, AsyncCallback callback,
InBlock.gif                                                        Object state);
InBlock.gif IAsyncResult BeginRequest(Message message, TimeSpan timeout,
InBlock.gif                                                        AsyncCallback callback, Object state);
InBlock.gif Message EndRequest(IAsyncResult result);
InBlock.gif Message Request(Message message);
InBlock.gif Message Request(Message message, TimeSpan timeout);
InBlock.gif
InBlock.gif EndpointAddress RemoteAddress { get; }
InBlock.gif Uri Via { get; }
InBlock.gif}
上面的代码里, Request方法接受一个Message类型的参数,然后返回一个Message类型的实例。 这些成员方法的签名保证了它们符合请求/应答消息交换模式。
接收接口:IReplyChannel
支持请求/应答消息交换模式的消息接收程序必须实现IReplyChannel接口,IReplyChannel的定义如下:
InBlock.gif public interface IReplyChannel : IChannel, ICommunicationObject {
InBlock.gif RequestContext ReceiveRequest();
InBlock.gif RequestContext ReceiveRequest(TimeSpan timeout);
InBlock.gif IAsyncResult BeginReceiveRequest(AsyncCallback callback, Object state);
InBlock.gif IAsyncResult BeginReceiveRequest(TimeSpan timeout,
InBlock.gif                                                                     AsyncCallback callback, Object state);
InBlock.gif RequestContext EndReceiveRequest(IAsyncResult result);
InBlock.gif
InBlock.gif Boolean TryReceiveRequest(TimeSpan timeout, out RequestContext context);
InBlock.gif IAsyncResult BeginTryReceiveRequest(TimeSpan timeout,
InBlock.gif                                                                            AsyncCallback callback,
InBlock.gif                                                                            Object state);
InBlock.gif Boolean EndTryReceiveRequest(IAsyncResult result,
InBlock.gif                                                             out RequestContext context);
InBlock.gif
InBlock.gif Boolean WaitForRequest(TimeSpan timeout);
InBlock.gif IAsyncResult BeginWaitForRequest(TimeSpan timeout,
InBlock.gif                                                                     AsyncCallback callback,
InBlock.gif                                                                     Object state);
InBlock.gif bool EndWaitForRequest(IAsyncResult result);
InBlock.gif
InBlock.gif EndpointAddress LocalAddress { get; }
InBlock.gif}
IReplyChannel里没有直接返回一个 Message实例的成员。相反,IReplyChannel接口支持通过RequestContext类型访问接收到的Message实例。下一节会详细讨论RequestContext类型。现在,我们该知道接收到的消息对于RequestContext类型是可见的,并且可以通过RequestContext访问消息实例。
像IInputChannel一样,IReplyChannel也定义了几类接收消息的方法。ReceiveRequest方法返回一个RequestContext实例,并且超时的时候,会抛出异常。TryReceiveRequest会返回一个Boolean类型的值来表示是否在规定的时间内接收到消息。WaitForRequest方法,和IInputChannel接口上的WaitForMessage方法类似,返回的结果取决于请求消息或是否超时。
请求/应答关联:RequestContext类型
在请求/应答消息交换模式里,请求和应答是紧密关联的。从发送者的角度来看,请求通常会返回一个应答消息。从接受者的角度来看,一个接收到的消息必须产生一个应答消息。如前所述,IReplyChannel使用RequestContext作为ReceiveRequest方法的返回类型。这是请求/应答消息交换模式下,接收通道关联消息的首要方式。
更高层次上,RequestContext类型包装了请求消息,而且提供了发送应答消息给发送者的方法。在RequestContext里,可以通过RequestMessage属性查看请求消息。RequestContext的Reply方法提供了发送应答消息的途径。和其它的通道成员一样,reply方法对于同步和异步方法都是可见的。下面代码展示了RequestContext类型的成员:
InBlock.gif public abstract class RequestContext : IDisposable {
InBlock.gif protected RequestContext();
InBlock.gif
InBlock.gif public abstract void Abort();
InBlock.gif
InBlock.gif public abstract void Reply(Message message);
InBlock.gif public abstract void Reply(Message message, TimeSpan timeout);
InBlock.gif public abstract IAsyncResult BeginReply(Message message,
InBlock.gif                                                                                    AsyncCallback callback,
InBlock.gif                                                                                    Object state);
InBlock.gif public abstract IAsyncResult BeginReply(Message message,
InBlock.gif                                                                                    TimeSpan timeout,
InBlock.gif                                                                                    AsyncCallback callback,
InBlock.gif                                                                                    Object state);
InBlock.gif public abstract void EndReply(IAsyncResult result);
InBlock.gif
InBlock.gif public abstract void Close();
InBlock.gif public abstract void Close(TimeSpan timeout);
InBlock.gif
InBlock.gif protected virtual void Dispose(Boolean disposing);
InBlock.gif void IDisposable.Dispose();
InBlock.gif
InBlock.gif public abstract Message RequestMessage { get; }
InBlock.gif}
如代码所示, RequestContext实现了IDisposable接口。因为通道层里其它成员没有实现IDisposable接口,所以这里就很难看出为什么RequestContext类型会这么干。RequestContext类型实现IDisposable接口是因为RequestContext包含了一个Message实例。第4章里“WCF101”里曾经讨论过,Message实例包含一个Stream,因此必须实现IDisposable接口。因为这种关系,RequestContext类型上的Dispose方法需要调用Message实例上的Dispose方法,这样才能销毁Message 实例拥有的Stream。记住RequestContext是一个抽象类型,因此继承RequestContext的类型可以根据需要提供自己的实现。
注释:与Message类型一样,RequestContext类型明确地实现了IDisposable接口
 
 

双工通道:IDuplexChannel

双工通道支持双工消息交换模式(MEP)。与数据报和请求/应答消息交换模式不同的是,双工消息交换模式运行发送者和接收者自由地发送和接受消息。我们在第3章里曾经看到,双工消息交换模式里的消息通信很像电话通话。在开始通信以前,发送者和接收者必须建立通信上下文环境。在,双工消息交换模式里,发送和接受通道形状是相同的,因此,发送者和接收者实现了相同的接口(假设连个消息参与者都是WCF程序)。因为双工消息交换模式的与生俱来的自由特性,以及接收者和发送公用相同的接口,因此只能通过发起通信来区分哪个是消息的发送者(就像只能通过谁先拨号,来确定谁是打电话的人一样)。
发送和接收接口:IDuplexChannel
IDuplexChannel接口实际上是IInputChannel和IOutputChannel的结合体。如前文所述,IInputChannel是为了实现了数据报消息接收者,而IOutputChannel是为了实现数据报发送者。因为支持双工通信的通道必须能够发送和接受消息,所以逻辑上,IDuplexChannel成员是数据报交换模式里使用的所有成员合并的结果。IDuplexChannel接口的定义如下:
 
 
 
  
  
  
 
InBlock.gif public interface IDuplexChannel : IInputChannel, IOutputChannel, IChannel,
InBlock.gif ICommunicationObject
InBlock.gif{
InBlock.gif}
IDefaultCommunicationTimeouts接口
因为大部分应用程序开发人员都不回直接接触通道,因此通道层必须有一种表示特定操作超时的方法。考虑到通道超时问题的时候,有 4个时间敏感的操作:打开通道、发送消息、接受消息和关闭通道。和通道层的其它功能一样,WCF类型系统包含一个描述超时的接口。System.ServiceModel. IDefaultCommunicationTimeouts,定义如下:
InBlock.gif public interface IDefaultCommunicationTimeouts {
InBlock.gif            TimeSpan CloseTimeout { get; }
InBlock.gif            TimeSpan OpenTimeout { get; }
InBlock.gif            TimeSpan ReceiveTimeout { get; }
InBlock.gif            TimeSpan SendTimeout { get; }
InBlock.gif}
IDefaultCommunicationTimeouts接口里每个成员的作用可以从你名字里推测出来。绑定、通道工厂和通道都实现了这个接口。因为绑定、通道工厂和通道实现了相同的接口,这些类型都可以传递超时到构造链中。例如,一个用户可以在 Binding里指定发送超时属性(Binding提供了setter器)。如果 Binding是消息发送者的一部分,它就可以把超时属性的值通过通道工厂的构造函数传递给通道工厂。同样,通道工厂也可以把超时属性的值传递给通道的构造函数。作用上看,这一系列的传递提供给用户可以通过API指定超时属性的能力,并且这些设置可以作用于通道层上。

ChannelBase类型

所有的自定义通道必须实现公共的状态机,并且暴露GetProperty<T>查询机制,实现一个或者多个通道形状,从通道工厂里接受一个超时设置。 System.ServiceModel.Channels.ChannelBase抽象类型就是这个目的,它确保了所有的通道成员的兼容性。下面代码展示了ChannelBase的类型定义:
 
 
InBlock.gif public abstract class ChannelBase : CommunicationObject,
InBlock.gif                                                                        IChannel,
InBlock.gif                                                                        ICommunicationObject,
InBlock.gif                                                                        IDefaultCommunicationTimeouts {
InBlock.gif // Constructor with channel factory parameter
InBlock.gif protected ChannelBase(ChannelManagerBase channelManager);
InBlock.gif // IChannel implementation
InBlock.gif public virtual T GetProperty<T>() where T: class;
InBlock.gif // CommunicationObject members
InBlock.gif protected override TimeSpan DefaultCloseTimeout { get; }
InBlock.gif protected override TimeSpan DefaultOpenTimeout { get; }
InBlock.gif protected override void OnClosed();
InBlock.gif protected TimeSpan DefaultReceiveTimeout { get; }
InBlock.gif protected TimeSpan DefaultSendTimeout { get; }
InBlock.gif // IDefaultCommunicationTimeouts implementation
InBlock.gif TimeSpan IDefaultCommunicationTimeouts.CloseTimeout { get; }
InBlock.gif TimeSpan IDefaultCommunicationTimeouts.OpenTimeout { get; }
InBlock.gif TimeSpan IDefaultCommunicationTimeouts.ReceiveTimeout { get; }
InBlock.gif TimeSpan IDefaultCommunicationTimeouts.SendTimeout { get; }
InBlock.gif // reference to channel factory
InBlock.gif protected ChannelManagerBase Manager { get; }
InBlock.gif private ChannelManagerBase channelManager;
InBlock.gif}
ChannelManagerBase的成员表示的是工厂创建通道的方法。第 7章“通道管理器”里会详细介绍这些内容。形状,假设ChannelManagerBase类型一直会从通道工厂里获取超时设置的值。注意ChannelBase里的TimeSpan类型的成员。以Default开头的成员都会从通道工厂里获取超时设置的值,而且这里明确实现了IDefaultCommunicationTimeouts的成员。如下所示:
InBlock.gif protected override TimeSpan DefaultOpenTimeout {
InBlock.gif get {
InBlock.gif     return ((IDefaultCommunicationTimeouts) this.channelManager).OpenTimeout;
InBlock.gif }
InBlock.gif}
InBlock.gif // delegate to DefaultOpenTimeout property TimeSpan
InBlock.gifIDefaultCommunicationTimeouts.OpenTimeout {
InBlock.gif get {
InBlock.gif         return this.DefaultOpenTimeout;
InBlock.gif }
InBlock.gif}
上面的代码仅仅介绍了通道里open如何实现超时值的传递。close、send和receive方法实现的方式类似。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值