1. 簡介
我思考了很多知識組織方法來幫助理解網(wǎng)絡(luò)知識,比如按osi模型從底至上身隐,或者按協(xié)議種類,或者按網(wǎng)絡(luò)發(fā)展史唯灵。但最終我還是決定選擇用這個經(jīng)典的問題贾铝,將網(wǎng)絡(luò)知識串成線。理解從輸入url到看到頁面的過程埠帕,弄明白這中間有哪些步驟垢揩,再仔細分析這些步驟的原理和行為,是我所能想到最清晰的一條知識脈絡(luò)了敛瓷。
2. 如何看到我們的頁面叁巨?
這里我們就以訪問阮一峰老師的一篇博文(HTTP 協(xié)議入門)來講解吧,大家閱讀本篇文章前也可以看看這篇文章呐籽。我們在瀏覽器輸入“http://www.ruanyifeng.com/blog/2016/08/http.html”以后锋勺,瀏覽器如何為我們展現(xiàn)網(wǎng)頁呢蚀瘸?
step1:瀏覽器解析url
瀏覽器會對我們輸入的url進行解析,主要將其分為下部分:協(xié)議宙刘、網(wǎng)絡(luò)地址苍姜、資源路徑。其中網(wǎng)絡(luò)地址指示該連接網(wǎng)絡(luò)上哪一臺計算機悬包,可以是域名或者IP地址,可以包括端口號馍乙;協(xié)議是從該計算機獲取資源的方式布近,常見的是HTTP,HTTPS丝格,F(xiàn)TP等撑瞧。不同協(xié)議有不同的通訊內(nèi)容格式;資源路徑指示從服務(wù)器上需要獲取資源的具體路徑显蝌。
這里瀏覽器對輸入的url解析為如下內(nèi)容:
url:http://www.ruanyifeng.com/blog/2016/08/http.html
協(xié)議:http
網(wǎng)絡(luò)地址(網(wǎng)站名):www.ruanyifeng.com
資源路徑:/blog/2016/08/http
當然预伺,瀏覽器還知道端口信息和參數(shù)信息,但這一步還用不上曼尊。另外網(wǎng)絡(luò)地址由服務(wù)器名:www和域名ruanyifeng.com組成酬诀。
step2:DNS域名解析
瀏覽器理解用戶輸入的信息,知道用戶想要用http訪問一個網(wǎng)絡(luò)地址是“www.ruanyifeng.com”的網(wǎng)站骆撇。那么如何找到這個地址呢瞒御,就像你打的回家,你跟司機說去阮老師家神郊,他哪兒知道阮老師家是哪里呢肴裙?你得告訴他門牌號呀。網(wǎng)站服務(wù)器的門牌號就是IP地址涌乳。所有瀏覽器首先要確認的是域名所對應(yīng)的服務(wù)器在哪里蜻懦。將域名解析成對應(yīng)的服務(wù)器IP地址這項工作,是由DNS服務(wù)器來完成的夕晓。
客戶端收到你輸入的域名地址后宛乃,它首先去找本地的hosts文件,檢查在該文件中是否有相應(yīng)的域名运授、IP對應(yīng)關(guān)系烤惊,如果有,則向其IP地址發(fā)送請求吁朦,如果沒有柒室,再去找DNS服務(wù)器。一般用戶很少去編輯修改hosts文件逗宜。
DNS服務(wù)器層級如下:
DNS服務(wù)器層級(轉(zhuǎn))
DNS查詢的具體步驟如下:
從瀏覽器緩存中查詢雄右。瀏覽器會存儲一定時間的DNS記錄空骚,操作系統(tǒng)不會告訴瀏覽器每個DNS記錄的保存時限,不同瀏覽器設(shè)置保存時限為一個固定值(不同瀏覽器情況不同擂仍,一般在2-30分鐘)囤屹。
從操作系統(tǒng)緩存中查詢。如果瀏覽器中沒有包含想要的緩存記錄逢渔,那瀏覽器就會發(fā)起操作系統(tǒng)請求肋坚,繼續(xù)查詢操作系統(tǒng)緩存
從路由器中查詢DNS緩存。請求持續(xù)發(fā)送到你的路由肃廓,它通常會有自己的DNS緩存智厌。
從ISP中查詢DNS緩存。下一個被查詢地方是ISP緩存DNS的服務(wù)器盲赊。
域名服務(wù)器迭代查詢铣鹏,根據(jù)返回的地址逐級向上查詢。首先從root域名服務(wù)器中查詢?nèi)?com域名服務(wù)器哀蘑,然后逐步向前查詢诚卸,.com頂級域名服務(wù)器到ruanyifeng的域名服務(wù)器。一般來說绘迁,.com級別的都已經(jīng)在緩存中了合溺,所以一般不會進行對root域名服務(wù)器的查詢。下面給出一張迭代查詢的圖脊髓。
DNS服務(wù)器遞歸查詢和迭代查詢(轉(zhuǎn))
瀏覽器客戶端向本地DNS服務(wù)器發(fā)送一個含有域名http://www.cnblogs.com的DNS查詢報文辫愉。本地DNS服務(wù)器把查詢報文轉(zhuǎn)發(fā)到根DNS服務(wù)器,根DNS服務(wù)器注意到其com后綴将硝,于是向本地DNS服務(wù)器返回comDNS服務(wù)器的IP地址恭朗。本地DNS服務(wù)器再次向comDNS服務(wù)器發(fā)送查詢請求,comDNS服務(wù)器注意到其http://www.cnblogs.com后綴并用負責該域名的權(quán)威DNS服務(wù)器的IP地址作為回應(yīng)依疼。最后痰腮,本地DNS服務(wù)器將含有http://www.cnblogs.com的IP地址的響應(yīng)報文發(fā)送給客戶端。
從客戶端到本地服務(wù)器屬于遞歸查詢律罢,而DNS服務(wù)器之間的交互屬于迭代查詢膀值。
正常情況下,本地DNS服務(wù)器的緩存中已有comDNS服務(wù)器的地址误辑,因此請求根域名服務(wù)器這一步不是必需的沧踏。
一些大型網(wǎng)站域名像wikipedia.org或者facebook.com整個域都映射到不止一個IP地址(土豪房子多),我們可以使用如下解決方法:
Round-robin DNS巾钉,DNS輪詢是其中一種方法翘狱,是DNS查找時返回多個IP時的解決方案。舉例來說砰苍,F(xiàn)acebook.com實際上就對應(yīng)了四個IP地址潦匈。
Load-balancer阱高,大型的網(wǎng)站一般都會使用高性能的負載均衡器來平衡流量。負載均衡器一直監(jiān)聽一個特殊的IP地址茬缩,并轉(zhuǎn)發(fā)請求到其他的服務(wù)器赤惊。(譯者注:簡單粗暴點理解就是在用戶和服務(wù)器之間加了個中間層,利用監(jiān)聽和轉(zhuǎn)發(fā)請求達到用戶相對快速訪問凰锡,資源最優(yōu)化使用的目的)
Geographic DNS未舟,基于地理的DNS,依賴客戶端的地理位置掂为,這是一個很好的存儲靜態(tài)資源的方法处面,不同的服務(wù)器可以不更新共享狀態(tài)。
Anycast菩掏,一種單個IP地址映射多個物理服務(wù)器的技術(shù)。
step3:瀏覽器獲取端口號
好了昵济,阮老師家的門牌號知道了智绸,正常來講是可以出發(fā)了》梅蓿可是對于網(wǎng)絡(luò)有些不一樣瞧栗,你還需要指定端口號。端口號之于計算機就像窗口號之于銀行海铆,一家銀行有多個窗口迹恐,每個窗口都有個號碼,不同窗口可以負責不同的服務(wù)卧斟。端口只是一個邏輯概念殴边,和計算機硬件沒有關(guān)系。現(xiàn)在可以這么說珍语,阮老師家好幾扇們锤岸,辦不同的業(yè)務(wù)走不同的門,你得告訴師傅你走那扇門板乙,你要不說是偷,就默認你是個普通客人,丟大門得了募逞。http協(xié)議默認端口號是80蛋铆。
step4:TCP建立連接
好了,IP和端口都有了放接,能出發(fā)了么刺啦?師傅很熱心,怕你去了對方家里沒人啊透乾,于是根據(jù)查到的信息給對方家里打了電話
師傅:“喂洪燥,家里有人沒翱某印?有客人來拜訪芭踉稀市咆?”
對方:“有啊,來吧”
師傅:“好嘞”再来。
在http消息發(fā)送前蒙兰,需要建立客戶端與服務(wù)器的TCP鏈接,也就是進行所謂的三次握手芒篷。
TCP是因特網(wǎng)中的傳輸層協(xié)議搜变,使用三次握手協(xié)議建立連接。當主動方發(fā)出SYN連接請求后针炉,等待對方回答SYN+ACK挠他,并最終對對方的 SYN 執(zhí)行 ACK 確認。這種建立連接的方法可以防止產(chǎn)生錯誤的連接篡帕,TCP使用的流量控制協(xié)議是可變大小的滑動窗口協(xié)議殖侵。
TCP三次握手
TCP三次握手的過程如下:
客戶端發(fā)送SYN(SEQ=x)報文給服務(wù)器端,進入SYN_SEND狀態(tài)镰烧。
服務(wù)器端收到SYN報文拢军,回應(yīng)一個SYN (SEQ=y)ACK(ACK=x+1)報文,進入SYN_RECV狀態(tài)怔鳖。
客戶端收到服務(wù)器端的SYN報文茉唉,回應(yīng)一個ACK(ACK=y+1)報文,進入Established狀態(tài)结执。
三次握手完成度陆,TCP客戶端和服務(wù)器端成功地建立連接,可以開始傳輸數(shù)據(jù)了昌犹。
step5: 發(fā)送HTTP請求
你們和阮老師家里互相確定了到訪的事情后坚芜,你們終于開心地出發(fā)了。也就是說斜姥,與服務(wù)器建立了連接后鸿竖,就可以向服務(wù)器發(fā)起請求了。這里我們先看下請求報文的結(jié)構(gòu):
請求報文結(jié)構(gòu)-轉(zhuǎn)
我們在chrome瀏覽器查看報文首部信息:
報文首部信息
我們可以從報文中看到發(fā)出的請求的具體信息铸敏。具體每個首部字段的作用缚忧,會開單章講解。
step6:服務(wù)器處理請求
好了杈笔,終于到了阮老師家闪水,坐車去阮老師家的例子到此就結(jié)束了。不過我們的渲染頁面的目的還沒達到蒙具,現(xiàn)在請求只是成功達到了服務(wù)器球榆,接下來服務(wù)器需要響應(yīng)瀏覽器的請求朽肥。
服務(wù)器端收到請求后的由web服務(wù)器(準確說應(yīng)該是http服務(wù)器)處理請求,諸如Apache持钉、Ngnix衡招、IIS等。web服務(wù)器解析用戶請求每强,知道了需要調(diào)度哪些資源文件始腾,再通過相應(yīng)的這些資源文件處理用戶請求和參數(shù),并調(diào)用數(shù)據(jù)庫信息空执,最后將結(jié)果通過web服務(wù)器返回給瀏覽器客戶端浪箭。下面以靜態(tài)渲染的頁面為例,ajax渲染不需要在服務(wù)器做頁面數(shù)據(jù)寫入辨绊。
以靜態(tài)頁面渲染為例
step7:返回響應(yīng)結(jié)果
在HTTP里奶栖,有請求就會有響應(yīng),哪怕是錯誤信息门坷。這里我們同樣看下響應(yīng)報文的組成結(jié)構(gòu):
響應(yīng)報文結(jié)構(gòu)-轉(zhuǎn)
在響應(yīng)結(jié)果中都會有個一個HTTP狀態(tài)碼驼抹,比如我們熟知的200、301拜鹤、404、500等流椒。通過這個狀態(tài)碼我們可以知道服務(wù)器端的處理是否正常敏簿,并能了解具體的錯誤。
狀態(tài)碼由3位數(shù)字和原因短語組成宣虾。根據(jù)首位數(shù)字惯裕,狀態(tài)碼可以分為五類:
狀態(tài)碼
具體的狀態(tài)碼信息我會開單章。
另外還有一些其他信息(chrome中顯示的response headers如下):
response headers
內(nèi)容編碼頭部告訴瀏覽器響應(yīng)體使用了gzip壓縮算法绣硝,解壓后就會看到你期望的HTML了蜻势。
除了壓縮信息之外,頭部還詳細說明了是否和怎么緩存頁面鹉胖、設(shè)置cookies(在這個響應(yīng)中沒有)握玛、隱秘信息等
或許有人注意到了設(shè)置了內(nèi)容類型為text/html,這部分頭部說明了瀏覽器將響應(yīng)內(nèi)容作為HTML渲染甫菠,而不是作為文件下載挠铲。瀏覽器將使用頭部決定如何解釋響應(yīng)結(jié)果,當然也會考慮其他因素寂诱,比如URL的擴展情況拂苹。
step8: 關(guān)閉TCP連接
為了避免服務(wù)器與客戶端雙方的資源占用和損耗,當雙方?jīng)]有請求或響應(yīng)傳遞時痰洒,任意一方都可以發(fā)起關(guān)閉請求瓢棒。
建立一個連接需要三次握手浴韭,而終止一個連接要經(jīng)過四次揮手,這是由TCP的半關(guān)閉(half-close)造成的脯宿。具體過程如下圖所示念颈。
四次揮手
(1) 某個應(yīng)用進程首先調(diào)用close,稱該端執(zhí)行“主動關(guān)閉”(active close)嗅绰。該端的TCP于是發(fā)送一個FIN分節(jié)舍肠,表示數(shù)據(jù)發(fā)送完畢。
(2) 接收到這個FIN的對端執(zhí)行 “被動關(guān)閉”(passive close)窘面,這個FIN由TCP確認翠语。
注意:FIN的接收也作為一個文件結(jié)束符(end-of-file)傳遞給接收端應(yīng)用進程,放在已排隊等候該應(yīng)用進程接收的任何其他數(shù)據(jù)之后财边,因為肌括,F(xiàn)IN的接收意味著接收端應(yīng)用進程在相應(yīng)連接上再無額外數(shù)據(jù)可接收。
(3) 一段時間后酣难,接收到這個文件結(jié)束符的應(yīng)用進程將調(diào)用close關(guān)閉它的套接字谍夭。這導(dǎo)致它的TCP也發(fā)送一個FIN。
(4) 接收這個最終FIN的原發(fā)送端TCP(即執(zhí)行主動關(guān)閉的那一端)確認這個FIN憨募。
既然每個方向都需要一個FIN和一個ACK紧索,因此通常需要4個分節(jié)。
注意:
(1) “通巢艘ィ”是指珠漂,某些情況下,步驟1的FIN隨數(shù)據(jù)一起發(fā)送尾膊,另外媳危,步驟2和步驟3發(fā)送的分節(jié)都出自執(zhí)行被動關(guān)閉那一端,有可能被合并成一個分節(jié)冈敛。[2]
(2) 在步驟2與步驟3之間待笑,從執(zhí)行被動關(guān)閉一端到執(zhí)行主動關(guān)閉一端流動數(shù)據(jù)是可能的,這稱為“半關(guān)閉”(half-close)抓谴。
(3) 當一個Unix進程無論自愿地(調(diào)用exit或從main函數(shù)返回)還是非自愿地(收到一個終止本進程的信號)終止時暮蹂,所有打開的描述符都被關(guān)閉,這也導(dǎo)致仍然打開的任何TCP連接上也發(fā)出一個FIN癌压。
無論是客戶還是服務(wù)器椎侠,任何一端都可以執(zhí)行主動關(guān)閉。通常情況是措拇,客戶執(zhí)行主動關(guān)閉我纪,但是某些協(xié)議,例如,HTTP/1.0卻由服務(wù)器執(zhí)行主動關(guān)閉浅悉。
step9:瀏覽器加載解析渲染
當瀏覽器獲得一個html文件時趟据,會“自上而下”加載妨马,并在加載過程中進行解析渲染下愈。
解析:
瀏覽器會將HTML解析成一個DOM樹,DOM 樹的構(gòu)建過程是一個深度遍歷過程:當前節(jié)點的所有子節(jié)點都構(gòu)建好后才會去構(gòu)建當前節(jié)點的下一個兄弟節(jié)點项玛。
將CSS解析成 CSS Rule Tree 荞估。
根據(jù)DOM樹和CSSOM來構(gòu)造 Rendering Tree咳促。注意:Rendering Tree 渲染樹并不等同于 DOM 樹,因為一些像 Header 或 display:none 的東西就沒必要放在渲染樹中了勘伺。
這一塊的過程比較復(fù)雜跪腹,會開單章。
step10:瀏覽器發(fā)送嵌入在HTML中的對象的請求
隨著瀏覽器渲染HTML飞醉,瀏覽器會注意到有些標簽需要請求其他URLs的資源冲茸,瀏覽器將會發(fā)送一個GET請求來重新獲取每個文件 。比如js文件缅帘,css文件轴术,圖片資源等。
每個URLs會像獲取HTML頁面的過程一樣獲取相應(yīng)資源钦无。所以逗栽,瀏覽器會在DNS中查詢域名,并向URL發(fā)送請求失暂,進行重定向(其實以上步驟我是省略了重定向這一步的)等等以上步驟
當然祭陷,靜態(tài)文件和動態(tài)網(wǎng)站不一樣,它們允許被瀏覽器緩存趣席。一些文件可能會根本不經(jīng)過服務(wù)器,直接被從緩存中取出醇蝴。因為響應(yīng)結(jié)果中返回一個包含著Expires頭的文件宣肚,所以瀏覽器知道要緩存一個文件多久。另外每個響應(yīng)可能包含著ETag頭悠栓,其作用類似版本號霉涨,如果瀏覽器發(fā)現(xiàn)已經(jīng)擁有了一個文件的ETag,那么就會立即停止此文件傳輸惭适。
step11:瀏覽器發(fā)送異步請求
在web2.0時代笙瑟,即使在頁面渲染后客戶端還是持續(xù)與服務(wù)器端通信。這個模式被稱為AJAX癞志。我會開單章講述往枷。
3. 總結(jié)
以上步驟只是大略地解析了從瀏覽器輸入url到最終頁面展示在用戶眼前的流程,更多細節(jié)我會開單章進行講解。