写在前面的话
网上讲Asp
net运行模式的好文章已经很多了
笔者本不用多此一举
另成一文
但从笔者自己的学习经验看
如果学到的这些知识不能对应到类库中的源代码
印象总归不够深刻
大有隔靴搔痒之感
只好自己写上一篇
对这方面的知识做个小小的总结
文中所有内容都是笔者在看了网上很多文章后
结合自己的开发经验得出的一些理解
难免有错误的地方
欢迎批评指出
另外
由于笔者能力所限
很多地方并未说透(真正对应到代码)
也盼高手能够给予补充
一进入Aspnet运行时之前
虽然本文的重点是对托管代码的解析但为了整个知识点的完整性这里简单介绍一下IIS处理请求的一些基本情况在一个IIS服务器上你可以设置多个应用程序池(每个应用程序池可以单独设置允许使用的最大内存数量CPU使用率回收工作进程的时间间隔等参数而且一个应用程序池里面只能使用一个版本的NET Framework)然后把自己的Web应用分别部署到这些应用程序池中在默认情况下每个应用池会有一个工作进程wwpexe来维护(如果开通了Web园功能也可以设置多个工作进程)每个应用程序(虚拟目录)在池中都有自己的应用程序域这些应用程序域都处于这个应用程序池的工作进程的进程空间内
IIS是通过各种ISAPI的扩展来处理各种类型的应用的当我们从客户端提交一个请求过来之后IIS会根据请求的页面或者服务的类型把请求映射到指定的ISAPI扩展比方说如果我们需要让IIS支持perl这样的服务器端程序(当然这个移植工作早就有人做过了)我们就需要编写一个专门处理对perl页面进行的请求的ISAPI扩展根据ISAPI的定义(符合这个定义的ISAPI扩展才能和IIS正常交互)在你的扩展中可以包括ISAPI Extension和ISAPI Filter两大部分ISAPI Extension是对请求的处理程序完成和web服务器之间的输入输出而ISAPI Filter则是一些回调接口你可以通过实现这些接口来介入到整个请求处理的每一步骤对AuthenticationRevolveCache等环节进行控制另外ISAPI本身就是在工作进程里运行的而aspnet运行时也是在工作进程里运行的所以两者的交互非常有效率
对于aspx页面这个扩展就是aspnet_isapidll因为这些ISAPI都是非托管的Win应用直接对它们进行改动是比较困难的所以为了增强Aspnet运行时的可扩展性aspnet_isapidll本身的功能非常少我们可以把aspnet_isapidll简单理解为请求信息的路由器负责把请求从IIS传送到aspnet运行时而后面我们将要讲到的HttpHandle和HttpModule则分别担负起了ISAPI Extension和ISAPI Filter的功能幸运的是HttpHandle和HttpModule可以由纯的托管代码来实现
二从非托管代码到托管代码
前面说了aspnet_isapidll是非托管代码而aspnet运行时是托管代码他们都运行在wwpexe工作进程里面那么两者之间的调用点发生在什么地方呢?在介绍接下来的内容之前必须先介绍一个概念ECBECB的全称是Extension Control Block它是一个非托管资源包具有对ISAPI接口完整的访问能力包含了所有和一个传入请求有关的底层信息如提交的标单中的数据等等所以说aspnet中的托管代码想要访问aspnet_isapidll对外提供的接口就需要通过ECB其实更准确的来说是托管代码公布了一个IUnknown类型的接口供aspnet_isapidll调用而aspnet_isapidll在调用的时候会把自己的ecb地址传进去
明白了ECB的概念下面我们要介绍一个接口和一个接口的实现类(位于SystemWebHosting名字空间下)请读者注意笔者在代码中的注释(本文的主要目的就是和大家一起从代码实现的角度来认识整个Aspnet运行时所以代码里的注释是笔者添加的关键性说明后面的所有代码段都是这样)
/**//*InterfaceType(ComInterfaceTypeInterfaceIsIUnknown)指明了这个接口将作为 IUnknown 派生接口向 COM 公开这就使得isapidll可以以COM方式调用此接口*/
[ComImport Guid(acfccabeaad) InterfaceType(ComInterfaceTypeInterfaceIsIUnknown)]
public interface IISAPIRuntime
{
void StartProcessing();
void StopProcessing();
/**//*ProcessRequest方法就是整个处理流程中托管代码和非托管代码的分界点可以看到里面是以一个IntPtr结构传入了调用方(也就是isapidll)的ECB地址*/
[return: MarshalAs(UnmanagedTypeI)]
int ProcessRequest([In] IntPtr ecb [In MarshalAs(UnmanagedTypeI)] int useProcessModel);
void DoGCCollect();
}
/**//*这个类实现了IISAPIRuntime接口它的实例对象存在于每一个AppDomain中作为整个Aspnet运行时的入口*/
public sealed class ISAPIRuntime : MarshalByRefObject IISAPIRuntime IRegisteredObject
{
// Fields
private static int _isThisAppDomainRemovedFromUnmanagedTable;
private static string s_thisAppDomainsIsapiAppId;
// Methods
[AspNetHostingPermission(SecurityActionDemand Level=AspNetHostingPermissionLevelMinimal) SecurityPermission(SecurityActionDemand Unrestricted=true)]
public ISAPIRuntime();
public void DoGCCollect();
public override object InitializeLifetimeService();
/**//*处理请求的入口点方法由isapidll以COM方式调用*/
public int ProcessRequest(IntPtr ecb int iWRType);
internal static void RemoveThisAppDomainFromUnmanagedTable();
internal void SetThisAppDomainsIsapiAppId(string appId);
public void StartProcessing();
public void StopProcessing();
void IRegisteredObjectStop(bool immediate);
}
所以一切都是从aspnet_isapidll以COM方式调用了一个ISAPIRuntime对象的ProcessRequest方法开始的可以多提一句的是这种调用是异步的也就是说aspnet_isapidll在调用后会立即返回但ECB会一直保留下来直到整个请求被处理完毕之后再释放
[] [] [] []