當我們在瀏覽器的地址欄輸入 www.linux178.com ,然后回車,回車這一瞬間到看到頁面到底發生了什么呢?
域名解析 --> 發起TCP的3次握手 --> 建立TCP連接后發起http請求 --> 服務器響應http請求,瀏覽器得到html代碼 --> 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片等) --> 瀏覽器對頁面進行渲染呈現給用戶
1.域名解析
首先Chrome瀏覽器會解析 www.linux178.com 這個域名(准確的叫法應該是主機名)對應的IP地址。怎么解析到對應的IP地址?
① Chrome瀏覽器 會首先搜索瀏覽器自身的DNS緩存(緩存時間比較短,大概只有1分鍾,且只能容納1000條緩存),看自身的緩存中是否有www.linux178.com 對應的條目,而且沒有過期,如果有且沒有過期則解析到此結束。
注:我們怎么查看Chrome自身的緩存?可以使用 chrome://net-internals/#dns 來進行查看
② 如果瀏覽器自身的緩存里面沒有找到對應的條目,那么Chrome會搜索操作系統自身的DNS緩存,如果找到且沒有過期則停止搜索解析到此結束.
注:怎么查看操作系統自身的DNS緩存,以Windows系統為例,可以在命令行下使用 ipconfig /displaydns 來進行查看
③ 如果在Windows系統的DNS緩存也沒有找到,那么嘗試讀取hosts文件(位於C:\Windows\System32\drivers\etc),看看這里面有沒有該域名對應的IP地址,如果有則解析成功。
④ 如果在hosts文件中也沒有找到對應的條目,瀏覽器就會發起一個DNS的系統調用,就會向本地配置的首選DNS服務器(一般是電信運營商提供的,也可以使用像Google提供的DNS服務器)發起域名解析請求(通過的是UDP協議向DNS的53端口發起請求,這個請求是遞歸的請求,也就是運營商的DNS服務器必須得提供給我們該域名的IP地址),運營商的DNS服務器首先查找自身的緩存,找到對應的條目,且沒有過期,則解析成功。如果沒有找到對應的條目,則有運營商的DNS代我們的瀏覽器發起迭代DNS解析請求,它首先是會找根域的DNS的IP地址(這個DNS服務器都內置13台根域的DNS的IP地址),找打根域的DNS地址,就會向其發起請求(請問www.linux178.com這個域名的IP地址是多少啊?),根域發現這是一個頂級域com域的一個域名,於是就告訴運營商的DNS我不知道這個域名的IP地址,但是我知道com域的IP地址,你去找它去,於是運營商的DNS就得到了com域的IP地址,又向com域的IP地址發起了請求(請問www.linux178.com這個域名的IP地址是多少?),com域這台服務器告訴運營商的DNS我不知道www.linux178.com這個域名的IP地址,但是我知道linux178.com這個域的DNS地址,你去找它去,於是運營商的DNS又向linux178.com這個域名的DNS地址(這個一般就是由域名注冊商提供的,像萬網,新網等)發起請求(請問www.linux178.com這個域名的IP地址是多少?),這個時候linux178.com域的DNS服務器一查,誒,果真在我這里,於是就把找到的結果發送給運營商的DNS服務器,這個時候運營商的DNS服務器就拿到了www.linux178.com這個域名對應的IP地址,並返回給Windows系統內核,內核又把結果返回給瀏覽器,終於瀏覽器拿到了www.linux178.com 對應的IP地址,該進行一步的動作了。
看下圖抓包截圖:
使用命令 wget www.linux178.com 來請求,發現直接使用chrome瀏覽器請求時,干擾請求比較多,所以就使用wget命令來請求,不過使用wget命令只能把index.html請求回來,並不會對index.html中包含的靜態資源(js、css等文件)進行請求。
2.發起TCP的3次握手
拿到域名對應的IP地址之后,User-Agent(一般是指瀏覽器)會根據url中輸入的端口向服務器的WEB程序(常用的有httpd,nginx等)發起TCP的連接請求。這個連接請求經過網絡中層層的路由到達機房路由之后,防火牆判斷當前端口是否開放,如果開放則進入到服務器網卡,然后是進入到內核的TCP/IP協議棧(用於識別該連接請求,解封包,一層一層的剝開),最終到達WEB程序(本文就以Nginx為例),最終建立了TCP/IP的連接。
如下圖:
3.建立TCP連接后發起http請求
進過TCP3次握手之后,瀏覽器發起了http的請求(看第12包),使用的http的方法 GET 方法,請求的URL是 / ,協議是HTTP/1.0
以上的報文是HTTP請求報文。
下面是Chrome發起的http請求報文頭部信息
其中
Accept 就是告訴服務器端,我接受那些MIME類型
Accept-Encoding 這個看起來是接受那些壓縮方式的文件
Accept-Lanague 告訴服務器能夠發送哪些語言
Connection 告訴服務器支持keep-alive特性
Cookie 每次請求時都會攜帶上Cookie以方便服務器端識別是否是同一個客戶端
Host 用來標識請求服務器上的那個虛擬主機,比如Nginx里面可以定義很多個虛擬主機
那這里就是用來標識要訪問那個虛擬主機。
User-Agent 用戶代理,一般情況是瀏覽器,也有其他類型,如:wget curl 搜索引擎的蜘蛛等
4.服務器端響應http請求,瀏覽器得到html代碼
看下圖 第12號包是http請求包,第32包是http響應包
服務器端WEB程序接收到http請求以后,就開始處理該請求,處理之后就返回給瀏覽器html文件。
第32號包 是服務器返回給客戶端http響應包(200 ok 響應的MIME類型是text/html),代表這一次客戶端發起的http請求已成功響應。200 代表是的 響應成功的狀態碼,還有其他的狀態碼如下:
1xx: 信息性狀態碼
100, 101
2xx: 成功狀態碼
200:OK
3xx: 重定向狀態碼
301: 永久重定向, Location響應首部的值仍為當前URL,因此為隱藏重定向;
302: 臨時重定向,顯式重定向, Location響應首部的值為新的URL
304:Not Modified 未修改,比如本地緩存的資源文件和服務器上比較時,發現並沒有修改,服務器返回一個304狀態碼,
告訴瀏覽器,你不用請求該資源,直接使用本地的資源即可。
4xx: 客戶端錯誤狀態碼
404: Not Found 請求的URL資源並不存在
5xx: 服務器端錯誤狀態碼
500: Internal Server Error 服務器內部錯誤
502: Bad Gateway 前面代理服務器聯系不到后端的服務器時出現
504:Gateway Timeout 這個是代理能聯系到后端的服務器,但是后端的服務器在規定的時間內沒有給代理服務器響應
用Chrome瀏覽器看到的響應頭信息:
Connection 使用keep-alive特性
Content-Encoding 使用gzip方式對資源壓縮
Content-type MIME類型為html類型,字符集是 UTF-8
Date 響應的日期
Server 使用的WEB服務器
那到底服務器端接收到http請求后是怎么樣生成html文件?
假設服務器端使用nginx+php(fastcgi)架構提供服務
① nginx讀取配置文件
我們在瀏覽器的地址欄里面輸入的是 http://www.linux178.com (http://可以不用輸入,瀏覽器會自動幫我們添加),其實完整的應該是http://www.linux178.com./ 后面還有個點(這個點代表就是根域,一般情況下我們不用輸入,也不顯示),后面的/也是不用添加,瀏覽器會自動幫我們添加(且看第3部那個圖里面的URL),那么實際請求的URL是http://www.linux178.com/,那么好了Nginx在收到 瀏覽器 GET / 請求時,會讀取http請求里面的頭部信息,根據Host來匹配 自己的所有的虛擬主機的配置文件的server_name,看看有沒有匹配的,有匹配那么就讀取該虛擬主機的配置,發現如下配置:
1
|
root
/web/echo
|
通過這個就知道所有網頁文件的就在這個目錄下 這個目錄就是/ 當我們http://www.linux178.com/時就是訪問這個目錄下面的文件,例如訪問http://www.linux178.com/index.html,那么代表/web/echo下面有個文件叫index.html
1
|
index index.html index.htm index.php
|
通過這個就能得知網站的首頁文件是那個文件,也就是我們在入http://www.linux178.com/ ,nginx就會自動幫我們把index.html(假設首頁是index.php 當然是會嘗試的去找到該文件,如果沒有找到該文件就依次往下找,如果這3個文件都沒有找到,那么就拋出一個404錯誤)加到后面,那么添加之后的URL是/index.php,然后根據后面的配置進行處理
1
2
3
4
5
6
7
|
location ~ .*\.php(\/.*)*$ {
root
/web/echo
;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
astcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
|
這一段配置指明凡是請求的URL中匹配(這里是啟用了正則表達式進行匹配) *.php后綴的(后面跟的參數)都交給后端的fastcgi進程進行處理。
② 把php文件交給fastcgi進程去處理
於是nginx把/index.php這個URL交給了后端的fastcgi進程處理,等待fastcgi處理完成后(結合數據庫查詢出數據,填充模板生成html文件)返回給nginx一個index.html文檔,Nginx再把這個index.html返回給瀏覽器,於是乎瀏覽器就拿到了首頁的html代碼,同時nginx寫一條訪問日志到日志文件中去。
5. 瀏覽器解析html代碼,並請求html代碼中的資源
瀏覽器拿到index.html文件后,就開始解析其中的html代碼,遇到js/css/image等靜態資源時,就向服務器端去請求下載(會使用多線程下載,每個瀏覽器的線程數不一樣),這個時候就用上keep-alive特性了,建立一次HTTP連接,可以請求多個資源,下載資源的順序就是按照代碼里的順序,但是由於每個資源大小不一樣,而瀏覽器又多線程請求請求資源,所以從下圖看出,這里顯示的順序並不一定是代碼里面的順序。
瀏覽器在請求靜態資源時(在未過期的情況下),向服務器端發起一個http請求(詢問自從上一次修改時間到現在有沒有對資源進行修改),如果服務器端返回304狀態碼(告訴瀏覽器服務器端沒有修改),那么瀏覽器會直接讀取本地的該資源的緩存文件。
6.瀏覽器對頁面進行渲染呈現給用戶
最后,瀏覽器利用自己內部的工作機制,把請求到的靜態資源和html代碼進行渲染,渲染之后呈現給用戶。
自此一次完整的HTTP事務宣告完成.
轉載自http://linux5588.blog.51cto.com/65280/1351007