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

轉自:https://huozhi.github.io/2015/03/15/network/2014-06-17-What-Happened-After-Search-URL-in-Browser/


瀏覽器解析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的輸出結果給請求者

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章