回顾上一篇,我们可以了解到以下内容:
1.默认情况下,只能上传小于4M的文件,如果我们要上传大文件的话,可以通过更改maxRequestLength来提高限制。
2.Asp.net 1.X 通过改变maxRequestLength可以增大上传的限制,但是由于需要将用户请求的实体内容完全载入内存后再处理,会大大影响服务器性能。
3.Asp.net 2.0 则会在用户请求的实体内容超出一定阈值或称限制值(256K)之后,被透明地缓冲到磁盘,因此在ASP.NET 2.0中服务器的内存不会因为客户端的异常请求而耗尽。
本篇概要:
在这一篇中主要理清HTTP请求流从到达WEB服务器开始到生成页面所经历的流程。为了能更好的理解本篇内空,建议大家先去看一下"HTTP请求流程"和"ASP.NET 应用程序生命周期";
正文内容:
有些朋友可能会不耐烦了,“这和上传大文件有什么关系呀?”。那是因为我们无法通过.Net提供给我们的上传控件得到我们想要的效果,如果想实现我们上传大文件并显示进度,那就只有在服务器接受到HTTP请求后,对该请求进行处理。那我们就得对HTTP请求在服务器端的流程有个了解。
浏览器发送页面请求(包括Get、Post、Put等请求方式)到IIS服务器后,在侦听程序进行接收后,只有少数几种被客户端请求的资源类型由IIS 直接处理。例如,对 HTML 页面、文本文件、JPEG 和 GIF 图像的传入请求由 IIS 处理。对 Active Server Page (*.asp) 文件的请求通过调用名为 asp.dll 的 ASP 专用扩展模块进行解析。同样,对 ASP.NET 资源(例如,*.aspx、*.asmx、*.ashx)的请求将传递到 ASP.NET ISAPI 扩展。因为IIS 6.0在IIS 5.x 上有所变动,所以我们分开来讲。
1.先来看IIS 5.X 的 ASP.net 请求处理过程:
由上图可知,IIS 5.X 中侦听程序由IIS进程(inetinfo.exe)来实现,它除了可以用来接收HTTP消息的功能外,而且直接把aspnet_isapi.dll(asp.net isapi扩展)寄宿在了该进程里。IIS 接收到消息后,检查脚本映射,然后调用 ASP.NET ISAPI 扩展,又由该扩展将请求和控制以及相关的所有信息传送给辅助进程aspnet_wp.exe(该辅助进程也是由asp.net isapi调用,并在该进程初始化时自动加载了.Net 运行时)中的.Net 运行时。因为ASP.NET ISAPI 扩展和.Net运行时不属于一个进程,所有的请求数据都通过命名管道进行发送。
2.接着我们再来看一下IIS 6.0 的 ASP.net 请求处理过程:
由图可知,在IIS 6.0中,inetinfo.exe不再用来传递HTTP请求到ISAPI扩展,但继续为其他协议的请求提供服务,取而代之的是采用名为 HTTP.sys 的Windows内核模式设备驱动程序实现,它并不处理它所接收到的请求而是将侦听到的HTTP 请求传送到正在运行网站的用户模式进程中(这里不再是aspnet_wp.exe,而是名为W3wp.exe 的可执行文件),在该辅助进程中加载了相应的ISAPI和.Net 运行时,这样就便于ISAPI模块与.Net 运行时环境的数据通信,从而避免了因进程间通讯所带来的损耗。
那么,当HTTP请求由ASP.NET ISAPI 扩展传送到.Net 运行时后,发生了什么事呢?
首先由ISAPIRuntime实例调用ProcessRequest方法。这个方法接收一个ECB(非托管对象,其中包含着所有底层的请求信息如服务器变量,HTTP输入流,HTTP输出流等)和一个服务器类型参数 iWRType(这个参数用于指定创建何种版本的ISAPIWorkerRequest),然后把它传给了ISAPIWorkerRequest对象,这个对象负责创建一个表示当前请求的HttpWorkerRequest对象。其中ISAPIWorkerRequest是一个继承自HttpWorkerRequest的抽象类,针对不同的IIS版本,具有不同的ISAPIWorkerRequest子类,比如:ISAPIWorkerRequestOutOfProc(IIS 5.x),ISAPIWorkerRequestInProcForIIS6 ,ISAPIWorkerRequestInProcForIIS7。ProcessRequest通过ISAPI传入的 iWRType 来创建不同HttpWorkerRequest,从而屏蔽了不同IIS的差异,这样后续的操作就不需要考虑这种差异了。
由上面的代码中大家可以看出最终是调用了ProcessRequestInternal方法,这个方法很重要,我们来看一下它主要做了些什么。
1. 首先检查当前HttpRuntime实例是否第一次被调用,如果是第一次调用则通过FirstRequestInit函数创建并初始化核心对象HttpContext。
HttpContext 类包含特定于当前应用程序请求的对象,如 HttpRequest 和 HttpResponse 对象。HttpRequest 对象包含有关当前请求的信息,包括 Cookie 和浏览器信息。HttpResponse 对象包含发送到客户端的响应,包括所有呈现的输出和 Cookie。
2.调用HttpResponse.InitResponseWriter函数初始化页面请求的返回对象HttpWorkerRequest.Response。
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.触发事件,实例化页面
由于知识点掌握不够,写这篇文章花了很长时间,但也让我深入地学习了下.Net运行时方面的知识。文章可能写得并不完善,因为所查找的资料在有些内容上有些小的出入,所以欢迎朋友们提出意见,
下一篇