瀏覽器解析 URL
一個標準的 URL 語法通常都建立在由 9 部分構成的通用格式上,瀏覽器會從使用者輸入的 URL 字串中解析對應的內容:
<schema>://<user>:<password>@<host>:<port>;<params>?<query>#<frag>
複製程式碼
其中比較重要的有:
- schema——協議版本
- host——主機地址
- port——埠號
- path——檔案路徑
- query——即 query_string 查詢字串
DNS 域名解析
如果上面提到的 host
部分為域名,則需要通過 DNS
對其進行解析。
查詢本地 host 檔案
使用者可以在本地的 host 檔案中指定域名和 IP 地址的對應關係,所以瀏覽器會先去本地的 host 檔案中尋找是否存在對應的 IP 地址,如果存在,則向該地址傳送請求。
DNS 解析
- 查詢本地域名伺服器
- 本地域名伺服器將查詢報文轉發到根域名伺服器並進行查詢
- 根域名伺服器根據具體的域名地址,向本地域名伺服器返回頂級域名伺服器地址
- 本地域名伺服器向頂級域名伺服器傳送查詢請求
- 頂級域名伺服器向本地域名伺服器返回許可權域名伺服器地址
- 本地域名伺服器向許可權域名伺服器傳送查詢請求
最終,我們通過 DNS 解析獲得了主機的 IP 地址
建立 TCP 連線
三次握手
三次握手的過程是老生常談了,資料比較多,這裡就不具體贅述了,只列出大致過程:
- 客戶端傳送
SYN
報文,請求建立連線 - 服務端傳送
ACK
報文和SYN
報文,表示同意客戶端的建立連線請求,同時自己也請求建立連線 - 客戶端傳送
ACK
報文,表示請求已收到
使用 APR 協議定位目標地址
在乙太網中,一臺主機要把資料幀傳送到同一區域網上的另一臺主機時,裝置驅動程式必須知道乙太網地址才能傳送資料。而我們只知道IP地址,這時就需要採用 ARP 協議將 IP 地址對映為乙太網地址。 在傳送第一個 SYN 報文時,IP 層會通過 ARP 協議查詢出目標主機的 MAC 地址。 (TCP/IP協議——ARP詳解)
在第一次握手時傳送的第一個 SYN
報文首先會通過 connect()
函式到達 IP 層,之後 IP 層會通過查詢路由表獲取目標主機的 MAC 地址並將其快取,然後該 MAC 地址會被通過 send()
函式交給網路介面進行封裝,最終將資料傳送出去。
PS:引用資料的文章非常詳細的解釋了不同情況下該過程的工作細節,建議認真閱讀一遍。
建立 SSL 隧道
如果請求使用的是 HTTPS
協議,則在建立 TCP 連線後,需要通過四次握手在其之上再建立一條加密隧道,即 SSL
。
四次握手
- 客戶端:
- 傳送協議版本號
- 傳送支援的加密方法
- 生成隨機數 1(
client random
)並傳輸給服務端
- 服務端:
- 選擇併傳送要使用的加密方法
- 傳送
數字證照
- 生成隨機數 2(
server random
)並傳輸給客戶端
- 客戶端:
- 驗證
數字證照
- 生成一個隨機數 3(
premaster secret
),使用公鑰對其進行加密後傳輸給服務端 - 使用
client random
、server random
、premaster secret
生成 會話金鑰(session key
)
- 驗證
- 伺服器:
- 使用私鑰對加密字串進行解密,獲得
premaster secret
- 使用
client random
、server random
、premaster secret
生成 會話金鑰(session key
)
- 使用私鑰對加密字串進行解密,獲得
四次握手通過非對稱加密的方式使客戶端和服務端獲得並持有相同的 session key
,並通過該 key 值對之後的會話過程進行對稱加密。
傳送 HTTP(s)請求
瀏覽器使用前兩步獲得的資訊構造請求報文
,並通過第三步建立起來的 TCP 連線向服務端傳送 HTTP 請求。 其中,請求報文
的基本格式為:
<method><request-URL><version>
<headers>
<entity-body>
複製程式碼
這三部分分別為起始行、首部和主體,通過瀏覽器請求發起的預設為 GET
請求,所以沒有主體部分。
下面展示了一個假想的 HTTP 報文,其中第一行為起始行,二三行為首部:
GET /index.html HTTP/1.1
Accept: text/html
Host: www.foo.com
複製程式碼
服務端代理處理請求
服務端代理即為 nginx
、apache
等伺服器軟體,它們會根據配置檔案將請求對映為伺服器上具體的檔案,並根據檔案型別對其進行處理返回。
直接返回靜態檔案(.html)
如果檔案是型別為 .html
、 .txt
、.xml
的靜態檔案,則只需將其內容作為響應的 entity-body
直接返回給客戶端。
解析動態檔案(.php)
如果檔案型別為 .php
、.jsp
、.asp
等動態檔案,則需要對其進行解析。這裡我們只講解對 .php
檔案的操作,以 nginx
伺服器為例。
- nginx 得知要處理的為 .php 檔案
- nginx 呼叫自己的
Fast-CGI
模組,構造 Fast-CGI 請求 - nginx 向
PHP-FPM
傳送 Fast-CGI 請求,此時 nginx 相當於一個反向代理伺服器 - PHP-FPM 的
master 程式
收到請求 - master 將請求分配給特定的
worker 程式
- worker 程式使用內嵌的
PHP-CGI
直譯器對 PHP 檔案進行解析,返回結果並生成對 nginx 的響應 - nginx 獲得來自 PHP-FPM 的響應,即得到靜態檔案
服務端代理響應請求
nginx 生成響應報文,返回給客戶端。響應報文和請求報文只有起始行的語法有所不同:
<version><status><reason-phrase>
<headers>
<entity-body>
複製程式碼
關閉 TCP 連線
老生常談喜聞樂見的四次揮手環節,因為相關資料實在是太多了,這裡也不具體贅述了,只列出大致過程:
- 客戶端傳送
FIN
報文,表示自己的所有資料已傳輸完畢 - 服務端返回
ACK
報文,表示請求已收到 - 服務端傳送
FIN
報文,表示自己的所有資料也都已經傳輸完畢 - 服務端返回
ACK
報文,表示請求已收到,並進入TIME_WAIT
狀態
客戶端對返回的檔案進行解析
瀏覽器會解析返回的 HTML / CSS / JS 等檔案,並最終將頁面展現在使用者面前。