前言
當我們在瀏覽器欄輸入:https://blog.csdn.net/dianxin113的時候冕碟,具體發(fā)生了什么呢拦惋?這個請求是怎么到達服務器及返回結果的呢?
概述
瀏覽器進行DNS域名解析安寺,得到對應的IP地址
根據(jù)這個IP厕妖,找到對應的服務器,發(fā)起TCP的三次握手
建立TCP連接后發(fā)起HTTP請求(一個完整的http請求報文)
服務器響應HTTP請求挑庶,瀏覽器得到html代碼(服務器如何響應)
瀏覽器解析html代碼言秸,并請求html代碼中的資源(如js、css迎捺、圖片等)(先得到html代碼举畸,才能去找這些資源)
瀏覽器對頁面進行渲染呈現(xiàn)給用戶
服務器關閉關閉TCP連接
一、DNS解析
a)首先會搜索瀏覽器自身的DNS緩存(緩存時間比較短凳枝,大概只有1分鐘抄沮,且只能容納1000條緩存)
b)如果瀏覽器自身的緩存里面沒有找到,那么瀏覽器會搜索系統(tǒng)自身的DNS緩存
c)如果還沒有找到岖瑰,那么嘗試從 hosts文件里面去找
d)在前面三個過程都沒獲取到的情況下叛买,瀏覽器就會發(fā)起一個DNS的系統(tǒng)調(diào)用,就會向本地配置的首選DNS服務器(一般是電信運營商提供的锭环,也可以使用像Google提供的DNS服務器)發(fā)起域名解析請求(通過的是UDP協(xié)議向DNS的53端口發(fā)起請求聪全,這個請求是遞歸的請求泊藕,也就是運營商的DNS服務器必須得提供給我們該域名的IP地址)
具體過程如下
<figcaption style="box-sizing: border-box; margin: 10px 0px 0px; padding: 0px; display: block; font-size: 0.7em; color: rgb(153, 153, 153); line-height: inherit; text-align: center;"></figcaption>
DNS優(yōu)化兩個方面:DNS緩存辅辩、DNS負載均衡
二、TCP三次握手
<figcaption style="box-sizing: border-box; margin: 10px 0px 0px; padding: 0px; display: block; font-size: 0.7em; color: rgb(153, 153, 153); line-height: inherit; text-align: center;"></figcaption>
三次握手完成之后這個TCP連接就進入Established狀態(tài)娃圆,就可以發(fā)起http請求了玫锋。
【問題1】:TCP 為什么需要3次握手?
2個計算機通信是靠協(xié)議(目前流行的TCP/IP協(xié)議)來實現(xiàn),如果2個計算機使用的協(xié)議不一樣讼呢,那是不能進行通信的撩鹿,所以這個3次握手就相當于試探一下對方是否遵循TCP/IP協(xié)議,協(xié)商完成后就可以進行通信了悦屏,當然這樣理解不是那么準確节沦。
【問題2】:為什么HTTP協(xié)議要基于TCP來實現(xiàn)键思?
目前在Internet中所有的傳輸都是通過TCP/IP進行的,HTTP協(xié)議作為TCP/IP模型中應用層的協(xié)議也不例外甫贯,TCP是一個端到端的可靠的面向連接的協(xié)議吼鳞,所以HTTP基于傳輸層TCP協(xié)議不用擔心數(shù)據(jù)的傳輸?shù)母鞣N問題。
三叫搁、 發(fā)起HTTP請求
HTTP是一個客戶端和服務器端請求和應答的標準(TCP)∑莅猓客戶端是終端用戶度苔,服務器端是網(wǎng)站坦康。通過使用Web瀏覽器、網(wǎng)絡爬蟲或者其它的工具雪位,客戶端發(fā)起一個到服務器上指定端口(默認端口為80)的HTTP請求。
通俗來講墓贿,他就是計算機通過網(wǎng)絡進行通信的規(guī)則茧泪,是一個基于請求與響應,無狀態(tài)的聋袋,應用層的協(xié)議队伟,常基于TCP/IP協(xié)議傳輸數(shù)據(jù)幽勒。目前任何終端(手機嗜侮,筆記本電腦。啥容。)之間進行任何一種通信都必須按照Http協(xié)議進行锈颗,否則無法連接。
<figcaption style="box-sizing: border-box; margin: 10px 0px 0px; padding: 0px; display: block; font-size: 0.7em; color: rgb(153, 153, 153); line-height: inherit; text-align: center;"></figcaption>
一個HTTP請求報文由請求行(request line)咪惠、請求頭部(header)击吱、空行和請求數(shù)據(jù)4個部分組成,下圖給出了請求報文的一般格式遥昧。
<figcaption style="box-sizing: border-box; margin: 10px 0px 0px; padding: 0px; display: block; font-size: 0.7em; color: rgb(153, 153, 153); line-height: inherit; text-align: center;"></figcaption>
-
請求行:用于描述客戶端的請求方式(GET/POST等)覆醇,請求的資源名稱(URL)以及使用的HTTP協(xié)議的版本號。
它們用空格分隔炭臭。例如永脓,GET /index.html HTTP/1.1。
HTTP協(xié)議的請求方法有GET鞋仍、POST常摧、HEAD、PUT、DELETE落午、OPTIONS谎懦、TRACE、CONNECT溃斋。GET是最常見的一種請求方式党瓮,當客戶端要從服務器中讀取文檔時,當點擊網(wǎng)頁上的鏈接或者通過在瀏覽器的地址欄輸入網(wǎng)址來瀏覽網(wǎng)頁的盐类,使用的都是GET方式寞奸。GET方法要求服務器將URL定位的資源放在響應報文的數(shù)據(jù)部分,回送給客戶端在跳。POST方法可以允許客戶端給服務器提供信息較多枪萄。POST方法將請求參數(shù)封裝在HTTP請求數(shù)據(jù)中,以名稱/值的形式出現(xiàn)猫妙,可以傳輸大量數(shù)據(jù)瓷翻,這樣POST方式對傳送的數(shù)據(jù)大小沒有限制,而且也不會顯示在URL中割坠,對隱私數(shù)據(jù)保密性更好齐帚。HEAD就像GET,只不過服務端接受到HEAD請求后只返回響應頭彼哼,而不會發(fā)送響應內(nèi)容对妄。當我們只需要查看某個頁面的狀態(tài)的時候,使用HEAD是非常高效的敢朱,因為在傳輸?shù)倪^程中省去了頁面內(nèi)容剪菱。
-
請求頭:請求頭部由關鍵字/值對組成,每行一對拴签,關鍵字和值用英文冒號“:”分隔孝常。請求頭部通知服務器有關于客戶端請求的信息,典型的請求頭有:
User-Agent:產(chǎn)生請求的瀏覽器類型蚓哩。Accept:客戶端可識別的內(nèi)容類型列表构灸。Host:請求的主機名,允許多個域名同處一個IP地址岸梨,即虛擬主機喜颁。Connection 告訴服務器支持keep-alive特性Cookie 每次請求時都會攜帶上Cookie以方便服務器端識別是否是同一個客戶端
空行:最后一個請求頭之后是一個空行,發(fā)送回車符和換行符盛嘿,通知服務器以下不再有請求頭
請求數(shù)據(jù):當使用POST等方法時洛巢,通常需要客戶端向服務器傳遞數(shù)據(jù)括袒。這些數(shù)據(jù)就儲存在請求正文中(GET方式是保存在url地址后面次兆,不會放到這里)。POST方法適用于需要客戶填寫表單的場合锹锰。與請求數(shù)據(jù)相關的最常使用的請求頭是Content-Type和Content-Length芥炭。
【問題3】:那什么是URL漓库、URI、URN园蝠?
URI Uniform Resource Identifier 統(tǒng)一資源標識符
URL Uniform Resource Locator 統(tǒng)一資源定位符
URN Uniform Resource Name 統(tǒng)一資源名稱
URL和URN 都屬于 URI渺蒿,為了方便就把URL和URI暫時都通指一個東西
四、服務器響應HTTP請求
接收到HTTP請求之后彪薛,就輪到負載均衡登場了茂装,它位于網(wǎng)站的最前端,把短時間內(nèi)較高的訪問量分攤到不同機器上處理善延。負載均衡方案有軟件少态、硬件兩種。軟件方案常見的就是NGINX了易遣。
Nginx的作用主要有兩個1彼妻,處理靜態(tài)文件請求,2轉發(fā)請求給后端服務器豆茫。然后后端服務器查詢數(shù)據(jù)庫返回數(shù)據(jù)侨歉。數(shù)據(jù)返回給客戶端仍然通過HTTP協(xié)議傳輸。
HTTP響應報文主要由狀態(tài)行揩魂、響應頭部幽邓、空行以及響應數(shù)據(jù)組成。
1.狀態(tài)行:由3部分組成火脉,分別為:協(xié)議版本颊艳,狀態(tài)碼,狀態(tài)碼描述忘分。
其中協(xié)議版本與請求報文一致棋枕,狀態(tài)碼描述是對狀態(tài)碼的簡單描述,所以這里就只介紹狀態(tài)碼妒峦。
一些常見的狀態(tài)碼如下:
狀態(tài)代碼為3位數(shù)字重斑。
1xx:指示信息–表示請求已接收,繼續(xù)處理肯骇。 2xx:成功–表示請求已被成功接收窥浪、理解、接受笛丙。 3xx:重定向–要完成請求必須進行更進一步的操作漾脂。 4xx:客戶端錯誤–請求有語法錯誤或請求無法實現(xiàn)。 5xx:服務器端錯誤–服務器未能實現(xiàn)合法的請求胚鸯。
2.響應頭:響應頭用于描述服務器的基本信息骨稿,以及客戶端如何處理數(shù)據(jù)
3.空格:CRLF(即 \r\n)分割
4.消息體:服務器返回給客戶端的數(shù)據(jù)
<figcaption style="box-sizing: border-box; margin: 10px 0px 0px; padding: 0px; display: block; font-size: 0.7em; color: rgb(153, 153, 153); line-height: inherit; text-align: center;"></figcaption>
上面的 HTTP 響應中,響應頭中的 Content-Length 同樣用于表示消息體的字節(jié)數(shù)。Content-Type 表示消息體的類型坦冠,通常瀏覽網(wǎng)頁其類型是HTML形耗,當然還會有其他類型,比如圖片辙浑、視頻等激涤。
五、瀏覽器解析
瀏覽器拿到index.html文件后判呕,就開始解析其中的html代碼倦踢,遇到js/css/image等靜態(tài)資源時,就向服務器端去請求下載(會使用多線程下載侠草,每個瀏覽器的線程數(shù)不一樣)硼一,這個時候就用上keep-alive特性了,建立一次HTTP連接梦抢,可以請求多個資源般贼,下載資源的順序就是按照代碼里的順序,但是由于每個資源大小不一樣奥吩,而瀏覽器又多線程請求請求資源哼蛆,所以從下圖看出,這里顯示的順序并不一定是代碼里面的順序霞赫。
瀏覽器在請求靜態(tài)資源時(在未過期的情況下)腮介,向服務器端發(fā)起一個http請求(詢問自從上一次修改時間到現(xiàn)在有沒有對資源進行修改),如果服務器端返回304狀態(tài)碼(告訴瀏覽器服務器端沒有修改)端衰,那么瀏覽器會直接讀取本地的該資源的緩存文件叠洗。
<figcaption style="box-sizing: border-box; margin: 10px 0px 0px; padding: 0px; display: block; font-size: 0.7em; color: rgb(153, 153, 153); line-height: inherit; text-align: center;"></figcaption>
六、瀏覽器進行頁面渲染
最后旅东,瀏覽器利用自己內(nèi)部的工作機制灭抑,把請求的靜態(tài)資源和html代碼進行渲染,渲染之后呈現(xiàn)給用戶抵代,瀏覽器是一個邊解析邊渲染的過程腾节。
首先瀏覽器解析HTML文件構建DOM樹,然后解析CSS文件構建渲染樹荤牍,等到渲染樹構建完成后案腺,瀏覽器開始布局渲染樹并將其繪制到屏幕上。
這個過程比較復雜康吵,涉及到兩個概念: reflow(回流)和repain(重繪)劈榨。DOM節(jié)點中的各個元素都是以盒模型的形式存在,這些都需要瀏覽器去計算其位置和大小等晦嵌,這個過程稱為relow;當盒模型的位置,大小以及其他屬性同辣,如顏色,字體,等確定下來之后拷姿,瀏覽器便開始繪制內(nèi)容,這個過程稱為repain邑闺。
頁面在首次加載時必然會經(jīng)歷reflow和repain。reflow和repain過程是非常消耗性能的棕兼,尤其是在移動設備上陡舅,它會破壞用戶體驗,有時會造成頁面卡頓伴挚。所以我們應該盡可能少的減少reflow和repain靶衍。
JS的解析是由瀏覽器中的JS解析引擎完成的。JS是單線程運行茎芋,JS有可能修改DOM結構颅眶,意味著JS執(zhí)行完成前,后續(xù)所有資源的下載是沒有必要的田弥,所以JS是單線程涛酗,會阻塞后續(xù)資源下載。
七偷厦、服務器關閉TCP連接
一般情況下商叹,一旦Web服務器向瀏覽器發(fā)送了請求數(shù)據(jù),它就要關閉TCP連接只泼。
而關閉TCP連接就需要進行四次揮手了剖笙。
中斷連接端可以是客戶端,也可以是服務器端请唱。
<figcaption style="box-sizing: border-box; margin: 10px 0px 0px; padding: 0px; display: block; font-size: 0.7em; color: rgb(153, 153, 153); line-height: inherit; text-align: center;"></figcaption>
第一次揮手:客戶端發(fā)送一個FIN=M弥咪,用來關閉客戶端到服務器端的數(shù)據(jù)傳送,客戶端進入FIN_WAIT_1狀態(tài)十绑。意思是說"我客戶端沒有數(shù)據(jù)要發(fā)給你了"聚至,但是如果你服務器端還有數(shù)據(jù)沒有發(fā)送完成,則不必急著關閉連接本橙,可以繼續(xù)發(fā)送數(shù)據(jù)晚岭。
第二次揮手:服務器端收到FIN后,先發(fā)送ack=M+1勋功,告訴客戶端坦报,你的請求我收到了,但是我還沒準備好狂鞋,請繼續(xù)你等我的消息片择。這個時候客戶端就進入FIN_WAIT_2 狀態(tài),繼續(xù)等待服務器端的FIN報文骚揍。
第三次揮手:當服務器端確定數(shù)據(jù)已發(fā)送完成字管,則向客戶端發(fā)送FIN=N報文啰挪,告訴客戶端,好了嘲叔,我這邊數(shù)據(jù)發(fā)完了亡呵,準備好關閉連接了。服務器端進入LAST_ACK狀態(tài)硫戈。
第四次揮手:客戶端收到FIN=N報文后锰什,就知道可以關閉連接了,但是他還是不相信網(wǎng)絡丁逝,怕服務器端不知道要關閉汁胆,所以發(fā)送ack=N+1后進入TIME_WAIT狀態(tài),如果Server端沒有收到ACK則可以重傳霜幼。服務器端收到ACK后嫩码,就知道可以斷開連接了∽锛龋客戶端等待了2MSL后依然沒有收到回復铸题,則證明服務器端已正常關閉,那好琢感,我客戶端也可以關閉連接了回挽。最終完成了四次握手。
上面是一方主動關閉猩谊,另一方被動關閉的情況千劈,實際中還會出現(xiàn)同時發(fā)起主動關閉的情況,也是通過四次握手牌捷。
【問題4】為什么連接的時候是三次握手墙牌,關閉的時候卻是四次握手?
答:因為當Server端收到Client端的SYN連接請求報文后暗甥,可以直接發(fā)送SYN+ACK報文喜滨。其中ACK報文是用來應答的,SYN報文是用來同步的撤防。但是關閉連接時虽风,當Server端收到FIN報文時,很可能并不會立即關閉SOCKET寄月,所以只能先回復一個ACK報文辜膝,告訴Client端,"你發(fā)的FIN報文我收到了"漾肮。只有等到我Server端所有的報文都發(fā)送完了厂抖,我才能發(fā)送FIN報文,因此不能一起發(fā)送克懊。故需要四步握手忱辅。
【問題5】為什么TIME_WAIT狀態(tài)需要經(jīng)過2MSL(最大報文段生存時間)才能返回到CLOSE狀態(tài)七蜘?
答:雖然按道理,四個報文都發(fā)送完畢墙懂,我們可以直接進入CLOSE狀態(tài)了橡卤,但是我們必須假象網(wǎng)絡是不可靠的,有可以最后一個ACK丟失损搬。所以TIME_WAIT狀態(tài)就是用來重發(fā)可能丟失的ACK報文碧库。在Client發(fā)送出最后的ACK回復,但該ACK可能丟失场躯。Server如果沒有收到ACK谈为,將不斷重復發(fā)送FIN片段旅挤。所以Client不能立即關閉踢关,它必須確認Server接收到了該ACK。Client會在發(fā)送出ACK之后進入到TIME_WAIT狀態(tài)粘茄。Client會設置一個計時器签舞,等待2MSL的時間。如果在該時間內(nèi)再次收到FIN柒瓣,那么Client會重發(fā)ACK并再次等待2MSL儒搭。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)。MSL指一個片段在網(wǎng)絡中最大的存活時間芙贫,2MSL就是一個發(fā)送和一個回復所需的最大時間搂鲫。如果直到2MSL,Client都沒有再次收到FIN磺平,那么Client推斷ACK已經(jīng)被成功接收魂仍,則結束TCP連接。
然而如果瀏覽器或者服務器在其頭信息加入了這行代碼:
Connection:keep-alive
TCP連接在發(fā)送后將仍然保持打開狀態(tài)拣挪,于是擦酌,瀏覽器可以繼續(xù)通過相同的連接發(fā)送請求。保持連接節(jié)省了為每個請求建立新連接所需的時間菠劝,還節(jié)約了網(wǎng)絡帶寬赊舶。
自此一次完整的HTTP事務宣告完成.
推薦閱讀