1. 程式人生 > >瀏覽器開啟一個網頁時都發生了什麼

瀏覽器開啟一個網頁時都發生了什麼

瀏覽器解析URL

選擇協議並找出你請求的資源,你可能開啟的是一個基於http協議的網站主頁
Protocol “http:” 使用HTTP協議
Resource “/“ 請求的資源是根目錄,一般是主頁

如果我位址列裡的不是url連結怎麼辦?(是的話跳過這步)

當協議或主機名不合法時,瀏覽器會將位址列中輸入的文字傳給預設的搜尋引擎。大部分情況下,在把文字傳遞給搜尋引擎的時候,URL會帶有特定的一串字元,用來告訴搜尋引擎這次搜尋來自這個特定瀏覽器

檢查HSTS列表(Https)

瀏覽器檢查自帶的“預載入HSTS(HTTP嚴格傳輸安全)”列表,這個列表裡包含了那些請求瀏覽器只使用HTTPS進行連線的網站

瀏覽器向網站發出第一個HTTP請求之後,網站會返回瀏覽器一個響應,請求瀏覽器只使用HTTPS傳送請求。然而,就是這第一個HTTP請求,卻可能會使使用者收到 downgrade attack 的威脅,這也是為什麼現代瀏覽器都預置了HSTS列表。

如果你沒有證書(certificate),肯定會gg的

轉換非ASCII的Unicode字元

瀏覽器檢查輸入是否含有不是 a-z, A-Z,0-9, - 或者 . 的字元
這裡主機名是 google.com,所以沒有非ASCII的字元,如果有的話,瀏覽器會對主機名部分使用 Punycode 編碼

DNS查詢

找hosts檔案
為了科學上網,很多人的hosts檔案都長得不行,這樣就一定程度上避免了某牆的DNS汙染
找本地DNS解析器快取
如果快取中沒有,就去呼叫 gethostbynme 庫函式進行查詢
如果hosts沒有這個域名的記錄,也沒有在本地DNS解析器快取裡找到,就去DNS伺服器找。DNS伺服器是由網路通訊棧提供的,通常是本地路由器或者ISP的快取DNS伺服器

查詢本地 DNS 伺服器會按照ARP協議(address resolution protocol)尋找,在另一篇部落格裡講過怎麼查

現在我們有了DNS伺服器或者預設閘道器的IP地址,我們可以繼續DNS請求了:

使用53埠向DNS伺服器傳送UDP請求包,如果響應包太大,會使用TCP
如果本地/ISP DNS伺服器沒有找到結果,它會發送一個遞迴查詢請求,一層一層向高層DNS伺服器做查詢,直到查詢到起始授權機構,如果找到會把結果返回

使用套接字

當瀏覽器得到了目標伺服器的IP地址,以及URL中給出來埠號(http協議預設埠號是80, https預設埠號是443),它會呼叫系統庫函式 socket ,請求一個 TCP流套接字,對應的引數是 AF_INET 和SOCK_STREAM 。

這個請求首先被交給傳輸層,在傳輸層請求被封裝成TCP segment。目標埠會會被加入頭部,源埠會在系統核心的動態埠範圍內選取(Linux下是ip_local_port_range)
TCP segment被送往網路層,網路層會在其中再加入一個IP頭部,裡面包含了目標伺服器的IP地址以及本機的IP地址,把它封裝成一個TCP packet。
這個TCP packet接下來會進入鏈路層,鏈路層會在封包中加入frame頭部,裡面包含了本地內建網絡卡的MAC地址以及閘道器(本地路由器)的MAC地址。像前面說的一樣,如果核心不知道閘道器的MAC地址,它必須進行ARP廣播來查詢其地址。

到了現在,TCP封包已經準備好了,可是使用下面的方式進行傳輸:

乙太網
WiFi
蜂窩資料網路

對於大部分家庭網路和小型企業網路來說,封包會從本地計算機出發,經過本地網路,再通過調變解調器把數字訊號轉換成模擬訊號,使其適於在電話線路,有線電視光纜和無線電話線路上傳輸。在傳輸線路的另一端,是另外一個調變解調器,它把模擬訊號轉換回數字訊號,交由下一個 網路節點 處理。節點的目標地址和源地址將在後面討論。

大型企業和比較新的住宅通常使用光纖或直接乙太網連線,這種情況下訊號一直是數字的,會被直接傳到下一個 網路節點 進行處理。

最終封包會到達管理本地子網的路由器。在那裡出發,它會繼續經過自治區域的邊界路由器,其他自治區域,最終到達目標伺服器。一路上經過的這些路由器會從IP資料報頭部裡提取出目標地址,並將封包正確地路由到下一個目的地。IP資料報頭部TTL域的值每經過一個路由器就減1,如果封包的TTL變為0,或者路由器由於網路擁堵等原因封包佇列滿了,那麼這個包會被路由器丟棄。

上面的傳送和接受過程在TCP連線期間會發生很多次:

客戶端選擇一個初始序列號(ISN),將設定了SYN位的封包傳送給伺服器端,表明自己要建立連線並設定了初始序列號
伺服器端接受到SYN包,如果它可以建立連線:
伺服器端選擇它自己的初始序列號
伺服器端設定SYN位,表明自己選擇了一個初始序列號
伺服器端把 (客戶端ISN + 1) 複製到ACK域,並且設定ACK位,表明自己接收到了客戶端的第一個封包
客戶端通過傳送下面一個封包來確認這次連線:
自己的序列號+1
接收端ACK+1
設定ACK位
資料通過下面的方式傳輸:
當一方傳送了N個Bytes的資料之後,將自己的SEQ序列號也增加N
另一方確認接收到這個資料包(或者一系列資料包)之後,它傳送一個ACK包,ACK的值設定為接收到的資料包的最後一個序列號
關閉連線時:
要關閉連線的一方傳送一個FIN包
另一方確認這個FIN包,並且傳送自己的FIN包
要關閉的一方使用ACK包來確認接收到了FIN

UDP 資料包

TLS 握手

客戶端傳送一個 Client hello 訊息到伺服器端,訊息中同時包含了它的TLS版本,可用的加密演算法和壓縮演算法。
伺服器端向客戶端返回一個 Server hello 訊息,訊息中包含了伺服器端的TLS版本,伺服器選擇了哪個加密和壓縮演算法,以及伺服器的公開證書,證書中包含了公鑰。客戶端會使用這個公鑰加密接下來的握手過程,直到協商生成一個新的對稱金鑰
客戶端根據自己的信任CA列表,驗證伺服器端的證書是否有效。如果有效,客戶端會生成一串偽隨機數,使用伺服器的公鑰加密它。這串隨機數會被用於生成新的對稱金鑰
伺服器端使用自己的私鑰解密上面提到的隨機數,然後使用這串隨機數生成自己的對稱主金鑰
客戶端傳送一個 Finished 訊息給伺服器端,使用對稱金鑰加密這次通訊的一個雜湊值
伺服器端生成自己的 hash 值,然後解密客戶端傳送來的資訊,檢查這兩個值是否對應。如果對應,就向客戶端傳送一個 Finished 訊息,也使用協商好的對稱金鑰加密
從現在開始,接下來整個 TLS 會話都使用對稱祕鑰進行加密,傳輸應用層(HTTP)內容

TCP 資料包

HTTP 協議

如果瀏覽器是Google出品的,它不會使用HTTP協議來獲取頁面資訊,而是會與伺服器端傳送請求,商討使用SPDY協議。

如果瀏覽器使用HTTP協議,它會向伺服器傳送這樣的一個請求:

GET / HTTP/1.1
Host: google.com
[其他頭部]

“其他頭部”包含了一系列的由冒號分割開的鍵值對,它們的格式符合HTTP協議標準,它們之間由一個換行符分割開來。這裡我們假設瀏覽器沒有違反HTTP協議標準的bug,同時瀏覽器使用 HTTP/1.1 協議,不然的話頭部可能不包含 Host 欄位,同時 GET 請求中的版本號會變成 HTTP/1.0 或者 HTTP/0.9 。

HTTP/1.1 定義了“關閉連線”的選項 “close”,傳送者使用這個選項指示這次連線在響應結束之後會斷開:

Connection:close

不支援持久連線的 HTTP/1.1 必須在每條訊息中都包含 “close” 選項。

在傳送完這些請求和頭部之後,瀏覽器傳送一個換行符,表示要傳送的內容已經結束了。

伺服器端返回一個響應碼,指示這次請求的狀態,響應的形式是這樣的:

200 OK
[response headers]

然後是一個換行,接下來有效載荷(payload),也就是 www.google.com 的HTML內容。伺服器下面可能會關閉連線,如果客戶端請求保持連線的話,伺服器端會保持連線開啟,以供以後的請求重用。

如果瀏覽器傳送的HTTP頭部包含了足夠多的資訊(例如包含了 Etag 頭部,以至於伺服器可以判斷出,瀏覽器快取的檔案版本自從上次獲取之後沒有再更改過,伺服器可能會返回這樣的響應:

304 Not Modified
[response headers]

這個響應沒有有效載荷,瀏覽器會從自己的快取中取出想要的內容。

在解析完HTML之後,瀏覽器和客戶端會重複上面的過程,直到HTML頁面引入的所有資源(圖片,CSS,favicon.ico等等)全部都獲取完畢,區別只是頭部的 GET / HTTP/1.1 會變成 GET /$(相對www.google.com的URL) HTTP/1.1 。

如果HTML引入了 www.google.com 域名之外的資源,瀏覽器會回到上面解析域名那一步,按照下面的步驟往下一步一步執行,請求中的 Host 頭部會變成另外的域名。

HTTP伺服器請求處理

HTTPD(HTTP Daemon)在伺服器端處理請求/相應。最常見的 HTTPD 有 Linux 上常用的 Apache 和 nginx,與 Windows 上的 IIS。

HTTPD接收請求

伺服器把請求拆分為以下幾個引數:

HTTP請求方法(GET, POST, HEAD, PUT 和 DELETE )。在訪問Google這種情況下,使用的是GET方法
域名:google.com
請求路徑/頁面:/ (我們沒有請求google.com下的指定的頁面,因此 / 是預設的路徑)

伺服器驗證其上已經配置了google.com的虛擬主機

伺服器驗證google.com接受GET方法

伺服器驗證該使用者可以使用GET方法(根據IP地址,身份資訊等)

如果伺服器安裝了 URL 重寫模組(例如 Apache 的 mod_rewrite 和 IIS 的 URL Rewrite),伺服器會嘗試匹配重寫規則,如果匹配上的話,伺服器會按照規則重寫這個請求

伺服器根據請求資訊獲取相應的響應內容,這種情況下由於訪問路徑是 “/” ,會訪問首頁檔案。(你可以重寫這個規則,但是這個是最常用的)

伺服器會使用指定的處理程式分析處理這個檔案,比如假設Google使用PHP,伺服器會使用PHP解析index檔案,並捕獲輸出,把PHP的輸出結果給請求者