您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何理解HTTP協議/IIS 原理及ASP.NET運行機制,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
前言
前一段在整理郵件的時候發現幾年前和CDD老師交流時的一份郵件.下面是簡單摘要:
“從技術角度來說,無論哪一個陣營,跟新技術都是不可避免的,也是很累的,當然作為一個程序員來說,也是必須的。要想讓技術的更新對自己的影響減小,基礎就必須打牢。所以,底層的東西和抽象層的東西需要下一番功夫。因為說到底,無論什么技術,無非就是架構和最終的實現,技術框架只是應用開發的一個平臺一種技術,如果了解了具體的東西,技術更新對你來說就沒什么影響了,或者換句話說,你要學一種新的技術,速度和效率會非常之高。”
上面一段話對自己的影響很大,可能大家在踏入“程序人生”的時候都會存在一些迷茫和彷徨。盡管我是屬于那種相當熱愛Proramming的一份子,但是面對萬花筒般的技術分支也曾徘徊猶豫過.徘徊之余要做的事情便是夯實基礎,尋找自己的興趣與方向.對技術的迭代,以不變應萬變才是王道.
正因為如此,所以也不會存在銀彈之說.如果真的有銀彈的話那么我信奉的是:程序=數據結構+算法
我選擇的方向是Web,也相信Web終究會是互聯網的未來.這篇文章簡單談一下自己對.NET平臺下Web基礎的一些淺解,由于自己水平有限,不足之處煩請見諒.
HTTP協議
HTTP協議是瀏覽器和服務器雙方共同遵循的規范.是一種基于TCP/IP(傳輸層協議,相對應的有UDP)的"應用層協議"
PS:TCP/UDP是廣泛使用的網絡通信協議,UDP協議具有不可靠性和不安全性,
相對來說TCP協議是基于連接和三次握手的(相對可靠與安全),然而B/S架構的網站,由于同時在線的人數會很多,如果都與服務器保持連接狀態.服務器的承載是相當大的,
因而衍生出HTTP協議.簡單的說:請求發起之后服務器端立刻關閉連接并釋放資源.也正因為如此,HTTP協議通常被理解為”無狀態”的.
當然維系"狀態"的手段有很多;如 Session/Cookie等 這里暫且不多做討論.
先來看一下典型的OSI七層模型 圖解
HTTP最通俗的理解 請求/響應.
圖示:
HTTP報文信息
HTTP Request Header
HTTP Response Header
當然,也可以通過設置改變瀏覽器的選項.這里不做詳細說明.不清楚的可以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 的圖示
根據上圖簡單分析下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幾個知識點:
HTTP.SYS:(Kernel)的一個組件,它負責偵聽(Listen)來自于外部的HTTP請求,根據請求的URL將其轉發給相應的應用程序池 (Application Pool)。當此HTTP請求處理完成時,它又負責將處理結果發送出去.為了提供更好的性能,HTTP.SYS內部建立了一個緩沖區,將最近的HTTP請求處理結果保存起來。
Application Pool: IIS總會保持一個單獨的工作進程:應用程序池。所有的處理都發生在這個進程里,包括ISAPI dll的執行。對于IIS6而言,應用程序池是一個重大的改進,因為它們允許以更小的粒度控制一個指定進程的執行。你可以為每一個虛擬目錄或者整個Web 站點配置應用程序池,這可以使你很容易的把每一個應用程序隔離到各自的進程里,這樣就可以把它與運行在同一臺機器上其他程序完全隔離。從Web處理的角度看,如果一個進程死掉,至少它不會影響到其它的進程。
當應用程序池接收到HTTP請求后,交由在此應用程序池中運行的工作者進程Worker Process: w3wp.exe來處理此HTTP請求。
Worker Process: 當工作者進程接收到請求后,首先根據后綴找到并加載對應的ISAPI擴展 (如:aspx 對應的映射是aspnet_isapi.dll),工作者進程加載完aspnet_isapi.dll后,由aspnet_isapi.dll負責加載 ASP.NET應用程序的運行環境即CLR (.NET Runtime)。
Worker Process運行在非托管環境,而.NET中的對象則運行在托管環境之上(CLR),它們之間的橋梁就是ISAPI擴展。
WAS(Web Admin Service):這是一個監控程序,它一方面可以存取放在InetInfo元數據庫(Metabase)中的各種信息,另一方面也負責監控應用程序池(Application Pool)中的工作者進程的工作狀態況,必要時它會關閉一個老的工作者進程并創建一個新的取而代之。
再來看下網上對IIS7經典模式下的圖解
IIS 7 應用程序池的托管管道模式“經典”模式也是這樣的工作原理。這種模式是兼容 IIS 6 的方式, 以減少升級的成本。
小插曲
場景假定:
截獲客戶端的請求,并對請求進行重寫。在IIS6中,請求的截獲動作只能被限制在IIS加載aspnet_isapi.dll后,也就是說:如果該請求不是明確針對asp.net資源的請求(比如這個請求只是一個靜態文件的請求,如www.cnblogs.com/index.html,這時我們就便不能在代碼中編寫截獲請求的邏輯,因為IIS6是根據URL的后綴來映射并加載對應的isapi的,如果一個請求的url 是:www.cnblogs.com/index.aspx,根據".aspx"這個后綴,IIS6可以得知這個請求是針對asp.net資源的,應該加載aspnet_isapi.dll創建.net運行時并運行asp.net頁面的代碼,但很明顯,諸如"www.cnblogs.com/index.html"這種請求,IIS6通常認為不是對asp.net資源的請求,因此不會加載 aspnet_isapi.dll來運行asp.net,我們即使在asp.net頁面中編寫了攔截請求的代碼,也不會被執行。當然,這里我說通常是有原因的,因為我們可以在IIS6中添加通配符程序映射的方式,或者在web.config中對某種請求手動添加處理程序的方式,來迫使IIS6為非 asp.net資源類型的請求加載aspnet_isapi.dll。IIS6中對請求的執行流程如上.
咦,有木有人和我一樣想到了URL Routing 和URL Rewriting ?
這里不做說明,大叔手記16傳送門:http://www.cnblogs.com/TomXu/archive/2011/12/27/2303486.html
這個問題先放一下~~了解II7的集成模式也許可以有一些思緒
讓我們再來看下IIS官網上對IIS7的圖解
傳送門 :http://www.iis.net/learn/get-started/introduction-to-iis/introduction-to-iis-architecture
1、當客戶端瀏覽器開始 HTTP 請求一個WEB 服務器的資源時,HTTP.sys 攔截到這個請求。
2、HTTP.sys 聯系 WAS 獲取配置信息。
3、WAS 向配置存儲中心(applicationHost.config)請求配置信息。
4、WWW 服務接收到配置信息,配置信息指類似應用程序池配置信息,站點配置信息等等。
5、WWW 服務使用配置信息去配置 HTTP.sys 處理策略。
6、WAS為請求創建一個進程(如果不存在的話)
7、工作者進程處理請求并對HTTP.sys做出響應.
8、客戶端接受到處理結果信息。
IIS 7 應用程序池的托管管道模式(集成模式)華麗的變身
IIS7中對asp.net的請求不再是分兩條處理管道,而是將asp.net和 IIS集成起來,這樣做的好處是統一了請求驗證工作,加強了asp.net對于請求的控制能力等等。在IIS7中,asp.net不再像IIS6一樣只限定于aspnet_isapi.dll中,而是被解放出來,從IIS接收到HTTP請求開始,即進入asp.net的控制范圍,asp.net可以存在于一個請求在IIS中各個處理階段。甚至可以為部署在IIS7中的PHP應用提供基于asp.net的驗證身份驗證功能(傳送門:http://msdn.microsoft.com/zh-cn/magazine/cc135973.aspx)。
好吧,戛然而止一下,篇幅有限:IIS部分告一段落 留一些遐想空間.
再來分析ASP.NET的運行機制
ASP.NET運行機制
在IIS6圖示中我們分析到“ AppManagerAppDomainFactory 的 Create 方法為 Application 創建一個 Application Domain;通過 ISAPIRuntime 的 ProcessRequest 處理 Request,進而將流程進入到 ASP.NET Http Runtime Pipeline。”
下面我們來看一下AppDomain運行過程圖示
AppDomain的作用,相信大家都很了解了吧.這里簡明扼要的寫幾點:
一個AppDomain中的代碼創建的對象不能由另一個AppDomain中的代碼直接訪問(只能使用按引用封送或者按值封送,起到了很好的隔離作用).
AppDomain可以卸載 CLR不支持從AppDomain中卸載一個程序集的能力,但可以告訴CLR卸載一個AppDomain,從而達到卸載當前包含在該AppDomain內的所有程序集.
AppDomain 可以單獨保護 當宿主加載一些代碼之后,可以保證這些代碼不會被破壞(或讀取)宿主本身使用的一些重要的數據結構.
AppDomain可以單獨配置 設置主要影響CLR在AppDomain中加載程序集的方式,涉及搜索路徑、版本綁定重定向、卷影復制及加載器的優化。
由以上幾點可以看出AppDomain確保了Windows系統及其中運行的應用程序的健壯性。AppDomain提供了保護、配置和終止其中每一個應用程序所需的隔離性。
再來看下ProcessRequest的過程
簡單分析一下上圖
ProcessRequest(HttpWorkerRequest wr)中判斷wr是否為null,然后判斷管線是否完整,再調用ProcessRequestNoDemand(wr)方法,
并判斷當前RequestQueue 是否為null,接著計算等待時間并更新管線數 CalculateWaitTimeAndUpdatePerfCounter(wr);
重置wr開始時間wr.ResetStartTime();調用ProcessRequestNow(wr)方法,并調用ProcessRequestInternal(wr)方法
繼續圖例
ProcessRequestInternal方法如下:
private void ProcessRequestInternal(HttpWorkerRequest wr) { HttpContext context; try { context = new HttpContext(wr, false);//由HttpWorkerRequest生成HttpContext } catch { //常見的400錯誤,就是在這里捕捉到滴 wr.SendStatus(400, "Bad Request"); wr.SendKnownResponseHeader(12, "text/html; charset=utf-8"); byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>"); wr.SendResponseFromMemory(bytes, bytes.Length); wr.FlushResponse(true); wr.EndOfRequest(); return; } wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context); Interlocked.Increment(ref this._activeRequestCount); HostingEnvironment.IncrementBusyCount(); try { try { this.EnsureFirstRequestInit(context); } catch { if (!context.Request.IsDebuggingRequest) { throw; } } context.Response.InitResponseWriter(); IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context); //得到HttpApplication if (applicationInstance == null) { throw new HttpException(System.Web.SR.GetString("Unable_create_app_object")); } if (EtwTrace.IsTraceEnabled(5, 1)) { EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, applicationInstance.GetType().FullName, "Start"); } if (applicationInstance is IHttpAsyncHandler) { IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance; context.AsyncAppHandler = handler2; handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);//屆時 HttpApplication處理請求 } else { applicationInstance.ProcessRequest(context); this.FinishRequest(context.WorkerRequest, context, null); } } catch (Exception exception) { context.Response.InitResponseWriter(); this.FinishRequest(wr, context, exception); } }
再看下GetApplicationInstance(context) 實例化Application的方法
View Code internal static IHttpHandler GetApplicationInstance(HttpContext context) { if (_customApplication != null) { return _customApplication; } if (context.Request.IsDebuggingRequest) { return new HttpDebugHandler(); } _theApplicationFactory.EnsureInited(); _theApplicationFactory.EnsureAppStartCalled(context); return _theApplicationFactory.GetNormalApplicationInstance(context); }
最后調用的GetNormalApplicationInstance方法中對當前空閑的application數目進行判斷,調用
application.InitInternal(context, this._state, this._eventHandlerMethods)方法,
this.InitModules()初始化所有的Modules,包含用戶自定義的HttpModules
this._stepManager.BuildSteps(this._resumeStepsWaitCallback);//管道事件序列
貼一下源碼:
internal override void BuildSteps(WaitCallback stepCallback) { ArrayList steps = new ArrayList(); HttpApplication app = base._application; bool flag = false; UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings; flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0); steps.Add(new HttpApplication.ValidatePathExecutionStep(app)); if (flag) { steps.Add(new HttpApplication.UrlMappingsExecutionStep(app)); } app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps); app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps); app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps); app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps); app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps); steps.Add(new HttpApplication.MapHandlerExecutionStep(app)); app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps); app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps); app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps); steps.Add(new HttpApplication.CallHandlerExecutionStep(app)); app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps); app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps); steps.Add(new HttpApplication.CallFilterExecutionStep(app)); app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps); this._endRequestStepIndex = steps.Count; app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps); steps.Add(new HttpApplication.NoopExecutionStep()); this._execSteps = new HttpApplication.IExecutionStep[steps.Count]; steps.CopyTo(this._execSteps); this._resumeStepsWaitCallback = stepCallback; }
到這里想必能夠使大家對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) 縮寫一樣.這個在寫文章的時候注意到過,但是沒有深入考慮. 理解不是很到位. 暫不妄下斷言. 歡迎斧正!! :-)
關于如何理解HTTP協議/IIS 原理及ASP.NET運行機制就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。