《WCF技术内幕》翻译32:第2部分_第6章_通道:通道状态机(1)

通道状态机

通道和通道工厂拥有相同的特性,这些特性独立于运行时功能。其中最重要的特性之一就是他们拥有公共的状态机。 WCF程序里的每个通道和通道工厂都有一个预定义的状态集合和一个预定义的方法集合,这些方法会控制通道和通道工厂在这些状态之间转换。

ICommunicationObject接口

在面向对象层次上,WCF类型系统强制实现了各个通道共用一个状态机,方式就是就是所有的通道和通道工厂都实现System.ServiceModel.ICommunicationObject接口。这个接口看起来也非常简单:
public interface ICommunicationObject {
 event EventHandler Closed;
 event EventHandler Closing;
 event EventHandler Faulted;
 event EventHandler Opened;
 event EventHandler Opening;
 
 
 void Abort();
 IAsyncResult BeginClose(AsyncCallback callback, object state);
 IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback,
                          Object state);
 IAsyncResult BeginOpen(AsyncCallback callback, object state);
 IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback,
                         Object state);
 void Close();
 void Close(TimeSpan timeout);
 void EndClose(IAsyncResult result);
 void EndOpen(IAsyncResult result);
 void Open();
 void Open(TimeSpan timeout);
 
  CommunicationState State { get; }
 
}
备注:本节里为了简洁起见,这里我只会把实现ICommunicationObject接口的类型作为通道,尽管通道工厂也实现了这个接口。
 
 
我们先看一下方法。为了打开、关闭和终止通道,ICommunicationObject声明了几个方法。这里重载了几个接受TimeSpan参数的异步Open和Close方法。理论上,无参的Open和Close方法会阻塞,直到通道最终打开或者关闭。实际上,这样不好,重载的方法带有一个TimeSpan参数,它表示愿意等待的通道对象打开或者关闭的时间。因为调用者不应该无限期等待通道打开或者关闭,所以无参的Open和Close方法应该传递一个默认TimeSpan值去调用重载后的方法。
ICommunicationObject接口还定义了符合微软异步编程模型的BeginOpen和BeginClose方法。因为打开和关闭一个通道可能要涉及到I/O,因此使用异步编程模型是个不错的方法。这样做意味着程序可以在打开和关闭通道的时候,使用线程池来实现资源的高效管理和线程调用,而不需要阻塞进程。这里重载了允许传递TimeSpan参数的BeginOpen和BeginClose方法来,和其它的方法很像,但是不同的是这些方法可以设置等待的时间。在打开和关闭通道的时候,我推荐使用ICommunicationObject接口里定义的异步方法。
ICommunicationObject接口也定义了一个CommunicationState类型的只读属性。这个成员是为了查询通道目前的状态。你会在下一节“CommunicationObject类型”里学习更多的通道状态机的内容。现在,我们来看一下CommunicationState状态值,如下所示,是个枚举类型:
public enum CommunicationState {
      Created,
      Opening,
      Opened,
      Closing,
      losed,
      Faulted
}
ICommunicationObject接口也定义了几个事件。如其它的.NET Framework事件一样,这些事件是其它对象接受通道状态改变通知的一种方法。事件的名字与状态类型CommunicationState相关。我们会在下一节详细学习这些事件。

CommunicationObject类型

其本身而言,实现ICommunicationObject接口对于通道或通道工厂的一致状态转换并不能带来什么帮助。相反,它保证了所有的通道和通道工厂拥有相同的成员。实际上,继承一个共同的基类能确保所有类型都保持行为上的一致性,而不是继承一个接口。抽象类型System.ServiceModel.Channels. CommunicationObject就是为了满足这个需求。
图6-2 :CommunicationObject 里内嵌的通道状态机
 
备注:本节里为了简洁起见,我只会把提到的CommunicationObject的子类型作为通道,尽管其它的类型也继承了这个类型。
 
 
CommunicationObject是所有通道的基类型,而且CommunicationObject类型实现了ICommunicationObject接口。Open、Close和Abort方法保证了通道状态可以按照一种连续的方式里转换。如图6-2. CommunicationObject不仅仅实现了ICommunicationObject接口,它也会在适当的时机激活事件、调用抽象和虚方法,它同时也提供了实现错误处理一致性帮助方法。下一节里会讲述CommunicationObject控制通道在不同状态间的转换过程。

CommunicationObject子类型

实际上,CommunicationObject的继承类型都应该使用CommunicationObject定义的状态机和它的错误处理成员,当然为了满足特定需求,也可以加入自己的实现。对于任何类型,盲目地继承一个基类型并不能确保恰当地使用基类型的功能。在构建一个通道的时候,非常重要的一点就是在合适的地方添加功能和正确调用基类型的方法。
CommunicationObject定义了几个虚方法。当继承这个类型的时候,需要子类型重写这些方法,这里十分重要的一点就是,继承类型调用基类CommunicationObject的成员,因为基类提供了控制状态转换和激活事件的实现。调用失败的话意味着状态不会正确地转换,当然也不会激活正确的事件,通道也没什么用处了。CommunicationObject的继承类型不是必须要重写这些方法。只是当这些继承类型需要有自己特别的实现的时候才会重写这些成员。
下面代码展示了CommunicationObject的虚方法,以及如何重写这些方法:
public abstract class CommunicationObject : ICommunicationObject {
 // virtual methods shown, others omitted
 protected virtual void OnClosed();
 protected virtual void OnClosing();
 protected virtual void OnFaulted();
 protected virtual void OnOpened();
 protected virtual void OnOpening();
}
 
 
sealed class CommunicationObjectDerivedType : CommunicationObject {
 // other methods omitted for clarity为了简洁,省略了一些方法
 protected override void OnClosed() {
    // implementation can occur before or after
    // the call to the base implementation
    base.OnClosed();
 }
 
 
 protected override void OnClosing() {
    // implementation can occur before or after
// the call to the base implementation
//代码会在调用基类的实现以前或后面执行
    base.OnClosing();
 }
protected override void OnOpened() {
    // implementation can occur before or after
    // the call to the base implementation
    base.OnOpened();
 }
 
 
protected override void OnOpening() {
    // implementation can occur before or after
    // the call to the base implementation
    base.OnOpening();
 }
 
 
protected override void OnFaulted() {
    // implementation can occur before or after
    // the call to the base implementation
    base.OnFaulted();
 }
}
CommunicationObject同样定义了几个抽象成员,它们是通道执行特别工作任务的主要方式。下面的代码列举这些抽象成员:
public abstract class CommunicationObject : ICommunicationObject {
 
 
 // abstract members shown, others omitted
 protected abstract void OnOpen(TimeSpan timeout);
 protected abstract IAsyncResult OnBeginOpen(TimeSpan timeout,
    AsyncCallback callback, Object state);
 protected abstract void OnEndOpen(IAsyncResult result);
 
 
 protected abstract void OnClose(TimeSpan timeout);
 protected abstract IAsyncResult OnBeginClose(TimeSpan timeout,
    AsyncCallback callback, Object state);
 protected abstract void OnEndClose(IAsyncResult result);
 
 
 protected abstract void OnAbort();
 
 
 protected abstract TimeSpan DefaultCloseTimeout { get; }
 
 
 protected abstract TimeSpan DefaultOpenTimeout { get; }
}
上面代码里最让我们感觉惊讶的应该就是DefaultCloseTimeout和DefaultOpenTimeout属性。规则上,当决定调用哪个成员方法的时候,我们通常会选择一个带有TimeSpan参数的方法。很显然,这样可以控制超时的时间范围。事实上,没有TimeSpan参数的方法,也是调用带有TimeSpan参数的方法。这时,使用的是默认的DefaultOpenTimeout和DefaultClosedTimeout值。
OnOpen、OnClose和OnAbort方法以及它们的同名方法,都是CommunicationObject的继承类型里定义对象初始化和资源回收代码的地方。比如,如果你要实现一个自定义通道,这个通道使用用户报文协议(UDP),初始化socket连接的代码要放在OnOpen和OnBeginOpen方法里。同样,关闭socket连接的代码也会放在OnClose、OnBeginClose和OnAbort方法里。
第一次接触通道和通道状态机的时候,容易令人迷惑的地方之一,就是CommunicationObject如何与其继承类型交互。我个人认为,理解这个交互过程是理解通道如何工作的第一步。后面的章节里会介绍CommunicationObject如何与其子类型在Open、Close、Abort和Fault方法中协调工作。为了说明以上这些内容,这里简要定义了一个上下文环境,代码如下:
sealed class App {
 static void Main() {
    MyCommunicationObject myCommObject = new MyCommunicationObject();
    // method invocations here这里调用方法
 }
}
sealed class MyCommunicationObject : CommunicationObject {
 // implementatation omitted for brevity简化起见省略代码实现部分
}

Open和BeginOpen方法

前面提到,CommunicationObject定义了Open 和BeginOpen方法来打开CommunicationObject的子类型。接下来本节里要描述的就是打开一个CommunicationObject子类型发生的事情:
MyCommunicationObject myCommObject = new MyCommunicationObject();
myCommObject.Open();
CommunicationObject:检查状态是否可以转换为Open
如果状态属性不是CommunicationObject.Created ,调用Open和 BeginOpen方法,就会抛出一个异常。CommunicationObject类型会调用ThrowIfDisposedOrImmutable保护方法来检查状态。如果CommunicationState是 CommunicationState.Opened 或CommunicationState.Opening状态,调用Open和BeginOpen方法都会抛出InvalidOperationException异常。同样,如果State是CommunicationState.Closed 或CommunicationState.Closing,调用Open和BeginOpen方法也会抛出ObjectDisposedException异常。值得注意的是,这些状态检查都是以线程安全的方式进行的。下面代码给出了CommunicationObject.Open方法的简单实现:
lock (this.thisLock){
 // check the state, throw an exception if transition is not OK
   //检查状态,如果转换失败,就会抛出异常
 this.ThrowIfDisposedOrImmutable();
     // other implementation shown in the next section下一节会介绍具体的实现
   }
CommunicationObject:如可以可以状态变为Opening
如果当前状态是CommunicationState.Created, State属性会变为CommunicationState.Opening。下面代码演示了CommunicationObject.Open方法如何把状态转换为CommunicationState.Opening的:
lock (this.thisLock){
 // check the state, throw an exception if transition is not OK
   //检查状态,如果转换失败,就会抛出异常
 this.ThrowIfDisposedOrImmutable();
     // transition the CommunicationState
     this.state = CommunicationState.Opening;
   }
MyCommunicationObject:调用虚方法OnOpening
如果CommunicationState的状态可以正常的转换为Opening,CommunicationObject.Open方法会调用CommunicationObject.OnOpening虚方法。如果CommunicationObject的子类型重写了这个方法的话,就会首先调用子类型的OnOpening方法。如前所述,子类型里重写OnOpening方法必须调用基类型CommunicationObject的OnOpening方法。
CommunicationObject:激活Opening事件,调用委托方法
调用CommunicationObject 类型的OnOpening方法会激活Opening事件,并调用事件引用的委托方法。这也是子类型为什么要调用CommunicationObject 的OnOpening方法的原因之一。如果这个过程失败,CommunicationObject.Open会抛出一个InvalidOperationException异常。
MyCommunicationObject:调用OnOpen虚方法
如果OnOpening没有抛出异常,CommunicationObject.Open方法会调用子类型的OnOpen方法。因为CommunicationObject定义抽象的OnOpen方法,子类型必须实现这个方法。如前面提到的,这就是那个包含大量CommunicationObject子类型初始化工作的方法。
MyCommunicationObject :调用 OnOpened 虚方法
如果OnOpen没有抛出异常,CommunicationObject.Open方法会调用OnOpened虚方法。如果子类型实现了OnOpened方法,就会调用子类型里重写的方法。就OnOpening方法而言,子类型调用CommunicationObject.OnOpened是非常重要的。如果调用失败会导致CommunicationObject.Open方法抛出一个InvalidOperationException异常。
CommunicationObject:状态转换为Opened
CommunicationObject.OnOpened方法,除了别的以外,会把CommunicationObject的State属性值修改为CommunicationState.Opened。这里状态轮换的前一个状态必须是CommunicationState.Opening。
CommunicationObject:激活Opened事件,调用委托方法
在状态转换为Opened以后,CommunicationObject.OnOpened方法会激活Opened事件,因此可以调用绑定的委托方法。

Close和Abort方法

CommunicationObject类型暴露了可以销毁对象的方法。通常,Close和BeginClose方法可以以一种优雅的方式CommunicationObject关闭对象,而Abort方法则会立即关闭对象。这里Close方法包含一个异步实现,而Abort方法没有。原因是两者的角色不同。例如,在正常情况下关闭对象是通过调用Close (或 BeginClose)方法实现的,当关闭对象的时候,CommunicationObject还能够执行I/O操作。为了说明这个问题,我们可以想一下在使用WS-ReliableMessaging (WS-RM)可靠消息传递的时候,调用Close方法,消息会继续发送、这时,Close方法会让负责WS-RM可靠消息传递的通道向另外的消息接受者发送一个TerminateSequence消息。换句话说,Close方法能够触发I/O。
另外一方面,调用Abort方法会立即关闭CommunicationObject通信对象并执行最小的I/O操作。因此,不需要任何的异步Abort方法的实现。值得一提的是Abort方法也不可以接受TimeSpan参数,而Close方法是可以的。
CommunicationObject和其子类型之间调用Close或BeginClose方法的协作模式与调用Open方法的协作模式十分相似。如前所述,调用CommunicationObject.Open方法会引起OnOpening、OnOpen和OnOpened方法的执行。同样,调用CommunicationObject.Close方法,也会引起OnClosing、OnClose和OnClosed方法的执行。下面代码简要说明了.NET Framework如何定义CommunicationObject.Close方法:
public void Close(TimeSpan timeout){
 // only general implementation shown
 this.OnClosing();
 this.OnClose(timeout);
 this.OnClosed();
}
此外,与激活Opening和Opened 事件的方式十分相似,CommunicationObject也也会激活Closing和 Closed事件。
Abort方法也会调用其它方法。下面代码块简要说明了.NET Framework如何定义CommunicationObject.Abort方法:
public void Abort(){
 // only general implementation shown
 this.OnClosing();
 this.OnAbort(); // only difference from Close
 this.OnClosed();
}
如上代码所示,Abort方法与调用Close方法里执行的代码十分相似。OnClosing和OnClosed会分别激活Closing和Closed事件。效果上,Abort方法与Close方法拥有一些相同的代码执行路径,并且会激活相同的事件。
记住CommunicationObject对象的主要工作就是维护一个一致的状态机,它说明了,Close和Abort方法执行路径的改变,都是基于正在关闭或者终止的对象的State属性。为了说明这个问题,思考一下我们调用一个状态为CommunicationState.Created的实例的Close方法。如果Open方法没被调用,Close和Abort方法的执行路径还有任何区别吗?记住,CommunicationObject的初始化工作都是在调用Open 或BeginOpen方法时完成的。在其中一个任何一个方法执行前,CommunicationObject对象都只是一个托管堆上的普通对象而已、在pre-open状态之前,CommunicationObject.Close方法和CommunicationObject.Abort方法执行的是相同的工作。可是,在Open或BeginOpen方法执行后,CommunicationObject对象可能会引用一些对象,比如连接的socket,而CommunicationObject. Close方法和CommunicationObject.Abort方法就执行不同的任务。表6-1描述了CommunicationObject的状态如何影响Close和Abort方法的执行过程。当你看这个表的时候,记住Close是一种关闭CommunicationObject对象的优雅方式,而Abort方法则显得过于粗暴。
表6-1 CommunicationState 、Close 和Abort 的对应关系
State属性
Close
Abort
CommunicationState.Created
调用 Abort
正常中止
CommunicationState.Opening
调用Abort
正常中止
CommunicationState.Opened
正常关闭
正常中止
CommunicationState.Closing
无操作
正常中止
CommunicationState.Closed
无操作  
无操作

Fault方法

受保护的Fault方法也是一种关闭CommunicationObject对象的方式,但它不属于ICommunicationObject接口。因为对于外部调用者来说,它是不可见的,所以它只适用于CommunicationObject对象的子类型,当这些子类型发现任何错误的时候,都可以立即关闭通道。调用Fault方法会把State属性转换为CommunicationState.Faulted,并且调用OnFaulted虚方法,因此CommunicationObject的子类型可以在重写的虚方法里定义自己的行为。大部分情况,OnFaulted方法都会调用Abort方法。

CommunicationObject堆栈

记住 CommunicationObject类型是所有通道和通道工厂的基类型。记住通道和通道工厂一般会组织为一个堆栈,并且只有顶部的通道对于调用者是可见的。在概念上,堆栈的组织顺序可以通过下面的类型查看:
internal sealed class MyCommunicationObject : CommunicationObject {
 private CommunicationObject _inner;
 internal MyCommunicationObject(CommunicationObject inner){
    this._inner = inner;
 }
 // other implementation omitted for brevity为了简洁起见,省略其它代码
}
MyCommunicationObject类型继承自CommunicationObject,它受CommunicationObject里定义的状态机的约束。确切地说,MyCommunicationObject对象有责任通过状态机与_inner成员变量同步转换。例如,如果调用者调用了MyCommunicationObject的Open方法,则MyCommunicationObject.Open的实现代码一定会调用inner成员变量的Open方法:
internal sealed class MyCommunicationObject : CommunicationObject {
 private CommunicationObject _inner;
 internal MyCommunicationObject(CommunicationObject inner){
    this._inner = inner;
 }   protected override void OnOpen(TimeSpan timeout) {
     // MyCommunicationObject.OnOpen implementation here
     // ...
     // Call Open on the inner member variable调用inner成员变量的Open方法
     // NOTE: may want to reduce timeout
     _inner.Open(timeout);
   }
   // other implementation omitted for brevity为了简洁起见,省略其它代码
}
当这样组织通道堆栈的时候, MyCommunicationObject.Open方法的调用者就不需要知道堆栈里所有的CommunicationObject节点,它们都会通过相同的状态机来以同步的方式实现状态转换。为了彻底说明问题,这里需要着重指出的是,在MyCommunicationObject.OnOpen 方法之前或者之后调用_inner.Open方法差别不大。不过,实际上,通常都是在方法结束的时候才调用的。另外,这里也许要调整传递给inner 成员变量的TimeSpan参数的值,来设置打开通道的超时范围。
【老徐备注】
1. CommunicationObject 类:
为系统中所有面向通信的对象(包括通道和通道工厂)公用的基本状态机提供通用的基实现。
2. CommunicationObject 方法:
 
名称
说明
公共方法
使通信对象立即从其当前状态转换到正在关闭状态。
公共方法
已重载。 开始一个异步操作以关闭通信对象。
公共方法
已重载。 开始一个异步操作以关闭通信对象。
公共方法
已重载。 使通信对象从其当前状态转换到关闭状态。
公共方法
完成一个异步操作以关闭通信对象。
公共方法
完成一个异步操作以打开通信对象。
公共方法
确定指定的 Object 是否等于当前的 Object (继承自 Object 。)
受保护的方法
使通信对象从其当前状态转换到出错状态。
受保护的方法
允许 Object " 垃圾回收 " 回收 Object 之前尝试释放资源并执行其他清理操作。 (继承自 Object 。)
受保护的方法
获取通信对象的类型。
公共方法
用作特定类型的哈希函数。 (继承自 Object 。)
公共方法
获取当前实例的 Type (继承自 Object 。)
受保护的方法
创建当前 Object 的浅表副本。 (继承自 Object 。)
受保护的方法
在派生类中实现时,在调用了一个同步中止操作,从而引起通信对象转换为正在关闭状态后,插入对通信对象的处理。
受保护的方法
在派生类中实现时,在调用了一个异步关闭操作,从而引起通信对象转换为正在关闭状态后,插入处理。
受保护的方法
在派生类中实现时,在调用了一个异步打开操作,从而引起通信对象转换为正在打开状态后,插入对通信对象的处理。
受保护的方法
在派生类中实现时,在调用了一个同步关闭操作,从而引起通信对象转换为正在关闭状态后,插入对通信对象的处理。
受保护的方法
在通信对象转换到正在关闭状态的过程中被调用。
受保护的方法
在通信对象转换到正在关闭状态的过程中被调用。
受保护的方法
在派生类中实现时,在通信对象关闭后完成异步操作。
受保护的方法
在派生类中实现时,在通信对象打开后完成异步操作。
受保护的方法
在调用了同步错误操作,从而引起通信对象转换为出错状态的情况下,该方法插入对通信对象的处理。
受保护的方法
在派生类中实现时,在通信对象转换为正在打开状态(此过程必须在指定时间间隔内完成)后,插入对通信对象的处理。
受保护的方法
在通信对象转换到已打开状态的过程中被调用。
受保护的方法
在通信对象转换到正在打开状态的过程中被调用。
公共方法
已重载。 使通信对象从已创建状态转换到已打开状态。
受保护的方法
如果通信对象已释放,则引发异常。
受保护的方法
如果通信对象的 State 属性未设置为 Created 状态,则引发异常。
受保护的方法
如果通信对象未处于 Opened 状态,则引发异常。
公共方法
返回表示当前 Object String (继承自 Object 。)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值