HTTP协议/IIS 原理及ASP.NET运行机制浅析

HTTP协议

HTTP协议是浏览器和服务器双方共同遵循的规范.是一种基于TCP/IP(传输层协议,相对应的有UDP)的"应用层协议"

PS:TCP/UDP是广泛使用的网络通信协议,UDP协议具有不可靠性和不安全性,

相对来说TCP协议是基于连接和三次握手的(相对可靠与安全),然而B/S架构的网站,由于同时在线的人数会很多,如果都与服务器保持连接状态.服务器的承载是相当大的,

因而衍生出HTTP协议.简单的说:请求发起之后服务器端立刻关闭连接并释放资源.也正因为如此,HTTP协议通常被理解为”无状态”的.

当然维系"状态"的手段有很多;如 Session/Cookie等 这里暂且不多做讨论.

先来看一下典型的OSI七层模型 图解

OSI2

HTTP最通俗的理解 请求/响应.

图示:

Http

HTTP报文信息

HTTP Request Header

HttpRequest

HTTP Response Header

HttpRespond

当然,也可以通过设置改变浏览器的选项.这里不做详细说明.不清楚的可以Google.微笑

给出ASP.NET下添加P3P头信息的例子

HttpContext.Current.Response.AddHeader("p3p", "CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""); 

有兴趣详细了解的可以参考 MSDN 中关于部署 P3P的文章。

下面是老生常谈的内容了(熟悉的朋友,自行跳过,权当温习下了 : )   )

请求头(消息头)包含(客户机请求的服务器主机名,客户机的环境信息等):

Accept:用于告诉服务器,客户机支持的数据类型  (例如:Accept:text/html,image/*)

Accept-Charset:用于告诉服务器,客户机采用的编码格式

Accept-Encoding:用于告诉服务器,客户机支持的数据压缩格式

Accept-Language:客户机语言环境

Host:客户机通过这个服务器,想访问的主机名

If-Modified-Since:客户机通过这个头告诉服务器,资源的缓存时间

Referer:客户机通过这个头告诉服务器,它(客户端)是从哪个资源来访问服务器的(防盗链)

User-Agent:客户机通过这个头告诉服务器,客户机的软件环境(操作系统,浏览器版本等)

Cookie:客户机通过这个头,将Coockie信息带给服务器

Connection:告诉服务器,请求完成后,是否保持连接

Date:告诉服务器,当前请求的时间

一个http响应代表服务器端向客户端回送的数据,它包括:

一个状态行,若干个响应消息头,以及实体内容

状态行:  例如:  HTTP/1.1  200 OK   (协议的版本号是1.1  响应状态码为200  响应结果为 OK)

响应头(消息头)包含:

Location:这个头配合302状态吗,用于告诉客户端找谁

Server:服务器通过这个头,告诉浏览器服务器的类型

Content-Encoding:告诉浏览器,服务器的数据压缩格式

Content-Length:告诉浏览器,回送数据的长度

Content-Type:告诉浏览器,回送数据的类型

Last-Modified:告诉浏览器当前资源缓存时间

Refresh:告诉浏览器,隔多长时间刷新

Content- Disposition:告诉浏览器以下载的方式打开数据。例如: context.Response.AddHeader("Content-Disposition","attachment:filename=icon.jpg");                                        context.Response.WriteFile("icon.jpg");

Transfer-Encoding:告诉浏览器,传送数据的编码格式

ETag:缓存相关的头(可以做到实时更新)

Expries:告诉浏览器回送的资源缓存多长时间。如果是-1或者0,表示不缓存

Cache-Control:控制浏览器不要缓存数据   no-cache

Pragma:控制浏览器不要缓存数据          no-cache

Connection:响应完成后,是否断开连接。  close/Keep-Alive

Date:告诉浏览器,服务器响应时间

IIS运行过程

有了上面的HTTP协议的知识回顾,下面来让我们看下IIS是怎样工作的?

IIS 5.X 已经距离我们很远了.好吧 XP默认的好像是的… 为万恶的IE6 默哀下0.0  .

这里我们来看一下IIS 6 的图示

IIS3

根据上图简单分析下IIS6的运行过程

在 User Mode 下,http.sys 接收到 http request,然后它会根据 IIS 中的 Metabase 查看基于该 Request 的 Application 属于哪个 Application Pool, 如果该 Application Pool 不存在,则创建之。否则直接将 request 发到对应 Application Pool 的 Queue中。

每个 Application Pool 对应着一个 Worker Process — w3wp.exe,(运行在 User Mode 下)。在 IIS Metabase 中维护着 Application Pool 和 Worker Process 的Mapping。WAS(Web Administrative Service)根据这样一个 mapping,将存在于某个 Application Pool Queue 的 request 传递到对应的 Worker Process (如果没有,就创建这样一个进程)。在 Worker Process 初始化的时候,加载 ASP.NET ISAPI,ASP.NET ISAPI 进而加载 CLR。最后通过 AppManagerAppDomainFactory 的 Create 方法为 Application 创建一个 Application Domain;通过 ISAPIRuntime 的  ProcessRequest 处理 Request,进而将流程进入到 ASP.NET Http Runtime Pipeline。

PS几个知识点:

  1. HTTP.SYS:(Kernel)的一个组件,它负责侦听(Listen)来自于外部的HTTP请求,根据请求的URL将其转发给相应的应用程序池 (Application Pool)。当此HTTP请求处理完成时,它又负责将处理结果发送出去.为了提供更好的性能,HTTP.SYS内部建立了一个缓冲区,将最近的HTTP请求处理结果保存起来。 

  2. Application Pool:  IIS总会保持一个单独的工作进程:应用程序池。所有的处理都发生在这个进程里,包括ISAPI dll的执行。对于IIS6而言,应用程序池是一个重大的改进,因为它们允许以更小的粒度控制一个指定进程的执行。你可以为每一个虚拟目录或者整个Web 站点配置应用程序池,这可以使你很容易的把每一个应用程序隔离到各自的进程里,这样就可以把它与运行在同一台机器上其他程序完全隔离。从Web处理的角度看,如果一个进程死掉,至少它不会影响到其它的进程。 
    当应用程序池接收到HTTP请求后,交由在此应用程序池中运行的工作者进程Worker Process: w3wp.exe来处理此HTTP请求。 

  3. Worker Process: 当工作者进程接收到请求后,首先根据后缀找到并加载对应的ISAPI扩展 (如:aspx 对应的映射是aspnet_isapi.dll),工作者进程加载完aspnet_isapi.dll后,由aspnet_isapi.dll负责加载 ASP.NET应用程序的运行环境即CLR (.NET Runtime)。 
    Worker Process运行在非托管环境,而.NET中的对象则运行在托管环境之上(CLR),它们之间的桥梁就是ISAPI扩展。 

  4. WAS(Web Admin Service):这是一个监控程序,它一方面可以存取放在InetInfo元数据库(Metabase)中的各种信息,另一方面也负责监控应用程序池(Application Pool)中的工作者进程的工作状态况,必要时它会关闭一个老的工作者进程并创建一个新的取而代之。 

再来看下网上对IIS7经典模式下的图解

IIS 7 应用程序池的托管管道模式“经典”模式也是这样的工作原理。这种模式是兼容 IIS 6 的方式, 以减少升级的成本。

2011101914255884

ASP.NET运行机制

在IIS6图示中我们分析到“ AppManagerAppDomainFactory 的 Create 方法为 Application 创建一个 Application Domain;通过 ISAPIRuntime 的  ProcessRequest 处理 Request,进而将流程进入到 ASP.NET Http Runtime Pipeline。”

下面我们来看一下AppDomain运行过程图示

appdomain3

AppDomain的作用,相信大家都很了解了吧.这里简明扼要的写几点:

一个AppDomain中的代码创建的对象不能由另一个AppDomain中的代码直接访问(只能使用按引用封送或者按值封送,起到了很好的隔离作用).

AppDomain可以卸载 CLR不支持从AppDomain中卸载一个程序集的能力,但可以告诉CLR卸载一个AppDomain,从而达到卸载当前包含在该AppDomain内的所有程序集.

AppDomain 可以单独保护 当宿主加载一些代码之后,可以保证这些代码不会被破坏(或读取)宿主本身使用的一些重要的数据结构.

AppDomain可以单独配置 设置主要影响CLR在AppDomain中加载程序集的方式,涉及搜索路径、版本绑定重定向、卷影复制及加载器的优化。

由以上几点可以看出AppDomain确保了Windows系统及其中运行的应用程序的健壮性。AppDomain提供了保护、配置和终止其中每一个应用程序所需的隔离性。

再来看下ProcessRequest的过程

HttpApplication_Analysis

简单分析一下上图

ProcessRequest(HttpWorkerRequest wr)中判断wr是否为null,然后判断管线是否完整,再调用ProcessRequestNoDemand(wr)方法,

并判断当前RequestQueue 是否为null,接着计算等待时间并更新管线数 CalculateWaitTimeAndUpdatePerfCounter(wr);

重置wr开始时间wr.ResetStartTime();调用ProcessRequestNow(wr)方法,并调用ProcessRequestInternal(wr)方法

继续图例

ProcessRequestInternal

ProcessRequestInternal方法如下:

  1. private void ProcessRequestInternal(HttpWorkerRequest wr)   

  2.   {   

  3.       HttpContext context;   

  4.       try   

  5.       {   

  6.           context = new HttpContext(wr, false);//由HttpWorkerRequest生成HttpContext   

  7.       }   

  8.       catch   

  9.       {  

  10.     

  11.         //常见的400错误,就是在这里捕捉到滴    

  12.           wr.SendStatus(400, "Bad Request");   

  13.           wr.SendKnownResponseHeader(12, "text/html; charset=utf-8");   

  14.           byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");   

  15.           wr.SendResponseFromMemory(bytes, bytes.Length);   

  16.           wr.FlushResponse(true);   

  17.           wr.EndOfRequest();   

  18.           return;   

  19.       }   

  20.       wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context);   

  21.       Interlocked.Increment(ref this._activeRequestCount);   

  22.       HostingEnvironment.IncrementBusyCount();   

  23.       try   

  24.       {   

  25.           try   

  26.           {   

  27.               this.EnsureFirstRequestInit(context);   

  28.           }   

  29.           catch   

  30.           {   

  31.               if (!context.Request.IsDebuggingRequest)   

  32.               {   

  33.                   throw;   

  34.               }   

  35.           }   

  36.           context.Response.InitResponseWriter();   

  37.           IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);   

  38.   //得到HttpApplication        

  39.     

  40.   if (applicationInstance == null)   

  41.           {   

  42.               throw new HttpException(System.Web.SR.GetString("Unable_create_app_object"));   

  43.           }   

  44.           if (EtwTrace.IsTraceEnabled(5, 1))   

  45.           {   

  46.               EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, applicationInstance.GetType().FullName, "Start");   

  47.           }   

  48.           if (applicationInstance is IHttpAsyncHandler)   

  49.           {   

  50.               IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;   

  51.               context.AsyncAppHandler = handler2;   

  52.               handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);//届时 HttpApplication处理请求   

  53.           }   

  54.           else   

  55.           {   

  56.               applicationInstance.ProcessRequest(context);   

  57.               this.FinishRequest(context.WorkerRequest, context, null);   

  58.           }   

  59.       }   

  60.       catch (Exception exception)   

  61.       {   

  62.           context.Response.InitResponseWriter();   

  63.           this.FinishRequest(wr, context, exception);   

  64.       }   

  65.   } 

再看下GetApplicationInstance(context) 实例化Application的方法

  1. View Code   

  2.  internal static IHttpHandler GetApplicationInstance(HttpContext context)  

  3.  {  

  4.      if (_customApplication != null)  

  5.      {  

  6.          return _customApplication;  

  7.      }  

  8.      if (context.Request.IsDebuggingRequest)  

  9.      {  

  10.          return new HttpDebugHandler();  

  11.      }  

  12.      _theApplicationFactory.EnsureInited();  

  13.      _theApplicationFactory.EnsureAppStartCalled(context);  

  14.      return _theApplicationFactory.GetNormalApplicationInstance(context);  

  15.  } 

最后调用的GetNormalApplicationInstance方法中对当前空闲的application数目进行判断,调用

application.InitInternal(context, this._state, this._eventHandlerMethods)方法,

this.InitModules()初始化所有的Modules,包含用户自定义的HttpModules

this._stepManager.BuildSteps(this._resumeStepsWaitCallback);//管道事件序列

贴一下源码:

  1. internal override void BuildSteps(WaitCallback stepCallback)   

  2.  {   

  3.      ArrayList steps = new ArrayList();   

  4.      HttpApplication app = base._application;   

  5.      bool flag = false;   

  6.      UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;   

  7.      flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0);   

  8.      steps.Add(new HttpApplication.ValidatePathExecutionStep(app));   

  9.      if (flag)   

  10.      {   

  11.          steps.Add(new HttpApplication.UrlMappingsExecutionStep(app));   

  12.      }   

  13.      app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);   

  14.      app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);   

  15.      app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);   

  16.      app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);   

  17.      app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);   

  18.      app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);   

  19.      app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);   

  20.      app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);   

  21.      steps.Add(new HttpApplication.MapHandlerExecutionStep(app));   

  22.      app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);   

  23.      app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);   

  24.      app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);   

  25.      app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);   

  26.      steps.Add(new HttpApplication.CallHandlerExecutionStep(app));   

  27.      app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);   

  28.      app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);   

  29.      app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);   

  30.      steps.Add(new HttpApplication.CallFilterExecutionStep(app));   

  31.      app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);   

  32.      app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);   

  33.      this._endRequestStepIndex = steps.Count;   

  34.      app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);   

  35.      steps.Add(new HttpApplication.NoopExecutionStep());   

  36.      this._execSteps = new HttpApplication.IExecutionStep[steps.Count];   

  37.      steps.CopyTo(this._execSteps);   

  38.      this._resumeStepsWaitCallback = stepCallback;   

  39.  } 

到这里想必能够使大家对ASP.NET管道机制能够有一个简单的回顾.当然还有很多地方没有详细分析。

再来总结一下IIS运行过程及ASP.NET管道机制:

Request→ (Internet )  HTTP.sys 监听 → WAS (IIS6 web Admin Service /IIS7 (Windows Activation Service) 接收请求

→(传入)Application Pool's → w3wp.exe(检查URL后缀)

→(加载)ISAPI扩展[aspnet_isapi.dll] → 注册映射

构造HttpRuntime类 →ProcessRequest方法

HttpContext实例产生(Request,Response,Session  and so on…)

HttpRuntime 调用 HttpApplicationFactory加载HttpApplication对象

穿越HttpModule到达HttpHandler

简单用140个字符(即一条微博的字数微笑)概括:

Request→ (Internet ) HTTP.sys →(WAS)→Application Pool's → w3wp.exe→ISAPI→ Map→ (Pipeline)HttpWorkerRequest→AppDomain→HttpRuntime→ProcessRequest()→ HttpContext(Request,Response)→ HttpRuntime→HttpApplicationFactory→HttpApplication→ HttpModule→HttpHandler→EndRequest

以上为个人学习摘要,如有错误,欢迎指正!!

补充

1:刚刚看到dudu发的一个闪存,里面提到了Application pool 与 AppDomain 的区别 来自stackoverflow,希望对大家有所帮助.

2:WAS缩写在IIS6中的指的是(Web Admin Service),在IIS7中指的是(Windows Activation Service)  缩写一样.