注: 以下理论是针对ASP.NET的
IIS与net是如何建立联系的?是通过ASPNET_ISAPI.dll这个文件建立联系的,那么这个dll在什么地方?在C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll中,不同的.net版本,aspnet_isapi.dll不同。
那么ASPNET_ISAPI.dll是怎么衔接IIS与net的呢?
首先由ASPNET_ISAPI.dll中的ISAPIRuntime实例调用ProcessRequest方法,这个方法接收一个ECB(非托管对象,其中包含着所有底层的请求信息如服务器变量,HTTP输入流,HTTP输出流等)和一个服务器类型参数 iWRType(这个参数用于指定创建何种版本的ISAPIWorkerRequest),然后把它传给了ISAPIWorkerRequest对象,这个对象负责创建一个表示当前请求的HttpWorkerRequest对象。其中ISAPIWorkerRequest是一个继承自HttpWorkerRequest的抽象类(HttpWorkerRequest也是抽象类)针对不同的IIS版本,具有不同的ISAPIWorkerRequest子类,比如:ISAPIWorkerRequestOutOfProc(IIS 5.x),ISAPIWorkerRequestInProcForIIS6 ,ISAPIWorkerRequestInProcForIIS7。
ProcessRequest通过ISAPI传入的 iWRType 来创建不同HttpWorkerRequest,从而屏蔽了不同IIS的差异,这样后续的操作就不需要考虑这种差异了。
//IntPtr表示的是指针,不同语言间通信是通过指针进行通信的,就是所谓的com组件,这个地址传的是浏览器传进来的数据,最后把数据封装成HttpWorkerRequest对象
public int ProcessRequest(IntPtr ecb, int iWRType){
int num;
try
{
HttpWorkerRequest wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, iWRType);
string appPathTranslated = wr.GetAppPathTranslated();
string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))
{//这里开始便把请求的处理流程就交给了HttpRuntime
HttpRuntime.ProcessRequestNoDemand(wr);
//在ProcessRequestNoDemand方法中所有的HTTP请求会被排成一个队列,顺次执行,并且最终会引发ProcessRequestInternal(HttpWorkerRequest wr)方法
return 0;
}
HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[] { appDomainAppPathInternal, appPathTranslated }));
num = 1;
}
catch (Exception exception)
{
Misc.ReportUnhandledException(exception, new string[] { SR.GetString("Failed_to_process_request") });
throw;
}
return num;
}
由上面的代码中大家可以看出最终是调用了ProcessRequestInternal方法,这个方法很重要,我们来看一下它主要做了些什么。
private void ProcessRequestInternal(HttpWorkerRequest wr)
{
HttpContext extraData = new HttpContext(wr, false);
wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, extraData);
Interlocked.Increment(ref this._activeRequestCount);
HostingEnvironment.IncrementBusyCount();
try
{
try
{
this.EnsureFirstRequestInit(extraData);
}
catch
{
if (!extraData.Request.IsDebuggingRequest)
{
throw;
}
}
extraData.Response.InitResponseWriter();
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(extraData);
if (applicationInstance == null)
{
throw new HttpException(SR.GetString("Unable_create_app_object"));
}
if (EtwTrace.IsTraceEnabled(5, 1))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, extraData.WorkerRequest, applicationInstance.GetType().FullName, "Start");
}
if (applicationInstance is IHttpAsyncHandler)
{
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
extraData.AsyncAppHandler = handler2;
handler2.BeginProcessRequest(extraData, this._handlerCompletionCallback, extraData);
}
else
{
applicationInstance.ProcessRequest(extraData);
this.FinishRequest(extraData.WorkerRequest, extraData, null);
}
}
catch (Exception exception)
{
extraData.Response.InitResponseWriter();
this.FinishRequest(wr, extraData, exception);
}
}
1. 首先检查当前HttpRuntime实例是否第一次被调用,如果是第一次调用则通过FirstRequestInit函数创建 并初始化核心对象HttpContext。
3.通过调用HttpApplicationFactory.GetApplicationInstance创建HttpApplication 类的实例并最终调用HttpApplication实例的InitInternal方法启动应用程序。
HttpApplication实例在InitInternal调用后,首先初始化Web.Config中注册的所有模块(HttpModule事件处理器,这个大家要尤其记住,因为我们真正开始编写代码的话,主要就是在这个模块中编写),然后调用HttpApplication 类的 Init 方法。接着会依次引发以下事件。
由上面第8个事件可以看出,页面在这个步骤被编译并创建了当前所请求的ASP.NET页面的实例(如果已经编译过,直接从缓存中加载)。
1.首先由inetinfo.exe接收到HTTP请求
2.检查脚本映射,然后调用 ASP.NET ISAPI 扩展
3.将消息送入aspnet_wp.exe进程,并加载运行时
4.调用ISAPIRuntime.ProcessRequest方法创建HttpWorkerRequest对象
6.创建HttpContext请求上下文
7.创建HttpApplication 类的实例
8.初始化Web.Config中注册的所有模块(HttpModule)
9.调用HttpApplication 类的 Init 方法
10.触发事件,实例化页面