靈魂拷問!瀏覽器輸入「xxxxhub」的背後.....

程式設計師cxuan發表於2021-03-30

Hey guys 各位讀者姥爺們大家好,這裡是程式設計師 cxuan 計算機網路連載系列的第 13 篇文章。

到現在為止,我們算是把應用層、運輸層、網路層和資料鏈路層都介紹完了,那麼現在是時候把這些內容都串起來,做一個全面的回顧了。那麼我這就以 Web 頁面的請求歷程為例,來和你聊聊計算機網路中這些協議是怎樣工作的、資料包是怎麼收發的,從輸入 URL 、敲擊會車到最終完成頁面呈現在你面前的這個過程。

首先,我開啟了 Web Browser ,然後在 Google 瀏覽器 URL 位址列中輸入了 maps.google.com

然後 ......

查詢 DNS 快取

瀏覽器在這個階段會檢查四個地方是否存在快取,第一個地方是瀏覽器快取,這個快取就是 DNS 記錄。

瀏覽器會為你訪問過的網站在固定期限內維護 DNS 記錄。因此,它是第一個執行 DNS 查詢的地方。 瀏覽器首先會檢查這個網址在瀏覽器中是否有一條對應的 DNS 記錄,用來找到目標網址的 IP 地址。

我是 chrome 瀏覽器,所以在 mac 中,無法使用 chrome://net-internals/#dns 找到對應的 IP 地址,在 windows 中是可以找到的。

那麼 mac 怎麼查詢 DNS 記錄呢?你可以使用 nslookup 命令來查詢,但這不是我們討論的重點。

DNS(Domain Name System) 是一個分散式的資料庫,它用於維護網址 URL 到其 IP 地址的對映關係。在網際網路中,IP 地址是計算機所能夠理解的一種地址,而 DNS 的這種別名地址是我們人類能夠理解和記憶的地址,DNS 就負責把人類記憶的地址對映成計算機能夠理解的地址,每個 URL 都有唯一的 IP 地址進行對應。

舉個例子,google 的官網是 www.google.com ,而 google 的 ip 地址是 216.58.200.228 ,這兩個地址你在 URL 上輸入哪個都能訪問,但是 IP 地址不好記憶,而 google.com 簡單明瞭。DNS 就相當於是我們幾年前使用的家庭電話薄,比如你想給 cxuan 打電話,你有可能記不住 cxuan 的電話號碼,此時你需要查詢電話薄來找到 cxuan 的電話號碼。

瀏覽器第二個需要檢查的地方就是作業系統快取。如果 DNS 記錄不在瀏覽器快取中,那麼瀏覽器將對作業系統發起系統呼叫,Windows 下就是 getHostName

在 Linux 和大部分 UNIX 系統上,除非安裝了 nscd,否則作業系統可能沒有 DNS 快取。

nscd 是 Linux 系統上的一種名稱服務快取程式

瀏覽低第三個需要檢查的地方是路由器快取,如果 DNS 記錄不在自己電腦上的話,瀏覽器就會和與之相連的路由器共同維護 DNS 記錄。

如果與之相連的路由器也沒有 DNS 記錄的話,瀏覽器就會檢查 ISP 中是否有快取。ISP 快取就是你本地通訊服務商的快取,因為 ISP 維護著自己的 DNS 伺服器,它快取 DNS 記錄的本質也是為了降低請求時間,達到快速響應的效果。一旦你訪問過某些網站,你的 ISP 可能就會快取這些頁面,以便下次快速訪問。對於經常看小電影的你是否感到震驚呢?如果家裡還安裝了一個可以聯網的攝像頭的話,那就有點嗨皮了。

你肯定比較困惑為什麼第一步瀏覽器需要檢查這麼多快取,你可能會感到不舒服,因為快取可能會透露我們的隱私,但是這些快取在調節網路流量和縮短資料傳輸時間等方面至關重要。

所以,上面涉及到 DNS 快取的查詢過程如下。

如果上面四個步驟中都不存在 DNS 記錄,那麼就表示不存在 DNS 快取,這個時候就需要發起 DNS 查詢,以查詢目標網址(本示例中是 maps.google.com)的 IP 地址。

發起 DNS 查詢

如上所述,如果想要使我的計算機和 maps.google.com 建立連線並進行通訊的話,我需要知道 maps.google.com 的 IP 地址,由於 DNS 的設計原因,本地 DNS 可能無法給我提供正確的 IP 地址,那麼它就需要在網際網路上搜尋多個 DNS 伺服器,來找到網站的正確 IP 地址。

這裡有個疑問,為什麼我需要搜尋多個 DNS 伺服器的來找到網站的 IP 地址呢?一臺伺服器不行嗎?

因為 DNS 是分散式域名伺服器,每臺伺服器只維護一部分 IP 地址到網路地址的對映,沒有任何一臺伺服器能夠維持全部的對映關係。

在 DNS 的早期設計中只有一臺 DNS 伺服器。這臺伺服器會包含所有的 DNS 對映。這是一種集中式的設計,這種設計並不適用於當今的網際網路,因為網際網路有著數量巨大並且持續增長的主機,這種集中式的設計會存在以下幾個問題

  • 單點故障(a single point of failure),如果 DNS 伺服器崩潰,那麼整個網路隨之癱瘓。
  • 通訊容量(traaffic volume),單個 DNS 伺服器不得不處理所有的 DNS 查詢,這種查詢級別可能是上百萬上千萬級,一臺伺服器很難滿足。
  • 遠距離集中式資料庫(distant centralized database),單個 DNS 伺服器不可能 鄰近 所有的使用者,假設在美國的 DNS 伺服器不可能臨近讓澳大利亞的查詢使用,其中查詢請求勢必會經過低速和擁堵的鏈路,造成嚴重的時延。
  • 維護(maintenance),維護成本巨大,而且還需要頻繁更新。

所以在當今網路情況下 DNS 不可能集中式設計,因為它完全沒有可擴充套件能力,所以採用分散式設計,這種設計的特點如下

分散式、層次資料庫

首先分散式設計首先解決的問題就是 DNS 伺服器的擴充套件性問題,因此 DNS 使用了大量的 DNS 伺服器,它們的組織模式一般是層次方式,並且分佈在全世界範圍內。沒有一臺 DNS 伺服器能夠擁有因特網上所有主機的對映。相反,這些對映分佈在所有的 DNS 伺服器上。

大致來說有三種 DNS 伺服器:根 DNS 伺服器頂級域(Top-Level Domain, TLD) DNS 伺服器權威 DNS 伺服器 。這些伺服器的層次模型如下圖所示

  • 根 DNS 伺服器 ,有 400 多個根域名伺服器遍及全世界,這些根域名伺服器由 13 個不同的組織管理。根域名伺服器的清單和組織機構可以在 https://root-servers.org/ 中找到,根域名伺服器提供 TLD 伺服器的 IP 地址。
  • 頂級域 DNS 伺服器,對於每個頂級域名比如 com、org、net、edu 和 gov 和所有的國家級域名 uk、fr、ca 和 jp 都有 TLD 伺服器或伺服器叢集。所有的頂級域列表參見 https://tld-list.com/ 。TDL 伺服器提供了權威 DNS 伺服器的 IP 地址。
  • 權威 DNS 伺服器,在因特網上具有公共可訪問的主機,如 Web 伺服器和郵件伺服器,這些主機的組織機構必須提供可供訪問的 DNS 記錄,這些記錄將這些主機的名字對映為 IP 地址。一個組織機構的權威 DNS 伺服器收藏了這些 DNS 記錄。

在瞭解了 DNS 伺服器的設計理念之後,我們回到 DNS 查詢的步驟上來,DNS 的查詢方式主要分為三種

DNS 查詢中會出現三種型別的查詢。通過組合使用這些查詢,優化的 DNS 解析過程可縮短傳輸距離。在理想情況下,可以使用快取的記錄資料,從而使 DNS 域名伺服器能夠直接使用非遞迴查詢。

  • 遞迴查詢:在遞迴查詢中,DNS 客戶端要求 DNS 伺服器(一般為 DNS 遞迴解析器)將使用所請求的資源記錄響應客戶端,或者如果解析器無法找到該記錄,則返回錯誤訊息。

  • 迭代查詢:在迭代查詢中,如果所查詢的 DNS 伺服器與查詢名稱不匹配,則其將返回對較低階別域名空間具有權威性的 DNS 伺服器的引用。然後,DNS 客戶端將對引用地址進行查詢。此過程繼續使用查詢鏈中的其他 DNS 伺服器,直至發生錯誤或超時為止。

  • 非遞迴查詢:當 DNS 解析器客戶端查詢 DNS 伺服器以獲取其有權訪問的記錄時通常會進行此查詢,因為其對該記錄具有權威性,或者該記錄存在於其快取內。DNS 伺服器通常會快取 DNS 記錄,查詢到來後能夠直接返回快取結果,以防止更多頻寬消耗和上游伺服器上的負載。

上面負責開始 DNS 查詢的介質就是 DNS 解析器,它一般是 ISP 維護的 DNS 伺服器,它的主要職責就是通過向網路中其他 DNS 伺服器詢問正確的 IP 地址。

如果想要了解更多關於 DNS 的訊息,請查閱 萬字長文爆肝 DNS 協議!

所以對於 maps.google.com 這個域名來說,如果 ISP 維護的伺服器沒有 DNS 快取記錄,它就會向 DNS 根伺服器地址發起查詢,根名稱伺服器會將其重定向到 .com 頂級域名伺服器。 .com 頂級域名伺服器會將其重定向到google.com 權威伺服器。google.com 名稱伺服器將在其 DNS 記錄中找到 maps.google.com 匹配的 IP 地址,並將其返回給您的 DNS 解析器,然後將其傳送回你的瀏覽器。

這裡值得注意的是,DNS 查詢報文會經過許多路由器和裝置才會達到根域名等伺服器,每經過一個裝置或者路由器都會使用路由表 來確定哪種路徑是資料包達到目的地最快的選擇。這裡面涉及到路由選擇演算法,如果小夥伴們想要了解路由選擇演算法,可以看看這篇文章 https://www.cisco.com/c/en/us/support/docs/ip/border-gateway-protocol-bgp/13753-25.html#anc3

ARP 請求

我看了很多篇文章都沒有提到這一點,那就是 ARP 請求的這個過程。

什麼時候需要傳送 ARP 請求呢?

這裡其實有個前提條件

  • 如果 DNS 伺服器和我們的主機在同一個子網內,系統會按照下面的 ARP 過程對 DNS 伺服器進行 ARP 查詢
  • 如果 DNS 伺服器和我們的主機在不同的子網,系統會按照下面的 ARP 過程對預設閘道器進行查詢

ARP 協議的全稱是 Address Resolution Protocol(地址解析協議),它是一個通過用於實現從 IP 地址到 MAC 地址的對映,即詢問目標 IP 對應的 MAC 地址 的一種協議。

簡而言之,ARP 就是一種解決地址問題的協議,它以 IP 地址為線索,定位下一個應該接收資料分包的主機 MAC 地址。如果目標主機不在同一個鏈路上,那麼會查詢下一跳路由器的 MAC 地址。

關於為什麼有了 IP 地址,還要有 MAC 地址概述可以參看知乎這個回答 https://www.zhihu.com/question/21546408

ARP 的大致工作流程如下

假設 A 和 B 位於同一鏈路,不需要經過路由器的轉換,主機 A 向主機 B 傳送一個 IP 分組,主機 A 的地址是 192.168.1.2 ,主機 B 的地址是 192.168.1.3,它們都不知道對方的 MAC 地址是啥,主機 C 和 主機 D 是同一鏈路的其他主機。

主機 A 想要獲取主機 B 的 MAC 地址,通過主機 A 會通過廣播 的方式向乙太網上的所有主機傳送一個 ARP 請求包,這個 ARP 請求包中包含了主機 A 想要知道的主機 B 的 IP 地址的 MAC 地址。

主機 A 傳送的 ARP 請求包會被同一鏈路上的所有主機/路由器接收並進行解析。每個主機/路由器都會檢查 ARP 請求包中的資訊,如果 ARP 請求包中的目標 IP 地址 和自己的相同,就會將自己主機的 MAC 地址寫入響應包返回主機 A

由此,可以通過 ARP 從 IP 地址獲取 MAC 地址,實現同一鏈路內的通訊。

所以,要想傳送 ARP 廣播,我們需要有一個目標 IP 地址,同時還需要知道用於傳送 ARP 廣播的介面的 MAC 地址。

這裡會涉及到 ARP 快取的概念。

現在你知道了傳送一次 IP 分組前通過傳送一次 ARP 請求就能夠確定 MAC 地址。那麼是不是每傳送一次都得經過廣播 -> 封裝 ARP 響應 -> 返回給主機這一系列流程呢?

想想看,瀏覽器是如何做的?瀏覽器內建了快取能夠快取你最近經常使用的地址,那麼 ARP 也是一樣的。ARP 高效執行的關鍵就是維護每個主機和路由器上的 ARP 快取(或表)。這個快取維護著每個 IP 到 MAC 地址的對映關係。通過把第一次 ARP 獲取到的 MAC 地址作為 IP 對 MAC 的對映關係到一個 ARP 快取表中,下一次再向這個地址傳送資料包時就不再需要重新傳送 ARP 請求了,而是直接使用這個快取表中的 MAC 地址進行資料包的傳送。每傳送一次 ARP 請求,快取表中對應的對映關係都會被清除。

通過 ARP 快取,降低了網路流量的使用,在一定程度上防止了 ARP 的大量廣播。

一般來說,傳送過一次 ARP 請求後,再次傳送相同請求的機率比較大,因此使用 ARP 快取能夠減少 ARP 包的傳送,除此之外,不僅僅 ARP 請求的傳送方能夠快取 ARP 接收方的 MAC 地址,接收方也能夠快取 ARP 請求方的 IP 和 MAC 地址,如下所示

不過,MAC 地址的快取有一定期限,超過這個期限後,快取的內容會被清除

深入理解 ARP 協議的話,可以參考 cxuan 的這篇文章。

ARP,這個隱匿在計網背後的男人


所以,瀏覽器會首先查詢 ARP 快取,如果快取命中,我們返回結果:目標 IP = MAC。

如果快取沒有命中:

  • 檢視路由表,看看目標 IP 地址是不是在本地路由表中的某個子網內。是的話,使用跟那個子網相連的介面,否則使用與預設閘道器相連的介面。
  • 查詢選擇的網路介面的 MAC 地址
  • 我們傳送一個資料鏈路層的 ARP 請求:

根據連線主機和路由器的硬體型別不同,可以分為以下幾種情況:

直連:

  • 如果我們和路由器是直接連線的,路由器會返回一個 ARP Reply (見下面)。

集線器:

  • 如果我們連線到一個集線器,集線器會把 ARP 請求向所有其它埠廣播,如果路由器也連線在其中,它會返回一個 ARP Reply

交換機:

  • 如果我們連線到了一個交換機,交換機會檢查本地 CAM/MAC 表,看看哪個埠有我們要找的那個 MAC 地址,如果沒有找到,交換機會向所有其它埠廣播這個 ARP 請求。
  • 如果交換機的 MAC/CAM 表中有對應的條目,交換機會向有我們想要查詢的 MAC 地址的那個埠傳送 ARP 請求
  • 如果路由器也連線在其中,它會返回一個 ARP Reply

ARP Reply:

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

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

(上述均來自:https://github.com/skyline75489/what-happens-when-zh_CN#dns)


封裝 TCP 資料包

瀏覽器得到目標伺服器的 IP 地址後,根據 URL 中的埠可以知道埠號 (http 協議預設埠號是 80, https 預設埠號是 443),會準備 TCP 資料包。資料包的封裝會經過下面的層層處理,資料到達目標主機後,目標主機會解析資料包,完整的請求和解析過程如下。

這裡就不再詳細介紹了,讀者朋友們可以閱讀 cxuan 的這篇文章 TCP/IP 基礎知識詳解詳細瞭解。

瀏覽器與目標伺服器建立 TCP 連線

在經過上述 DNS 和 ARP 查詢流程後,瀏覽器就會收到一個目標伺服器的 IP 和 MAC地址,然後瀏覽器將會和目標伺服器建立連線來傳輸資訊。這裡可以使用很多種 Internet 協議,但是 HTTP 協議建立連線所使用的運輸層協議是 TCP 協議。所以這一步驟是瀏覽器與目標伺服器建立 TCP 連線的過程。

TCP 的連線建立需要經過 TCP/IP 的三次握手,三次握手的過程其實就是瀏覽器和伺服器交換 SYN 同步和 ACK 確認訊息的過程。

假設圖中左端是客戶端主機,右端是服務端主機,一開始,兩端都處於CLOSED(關閉)狀態。

  1. 服務端程式準備好接收來自外部的 TCP 連線。然後服務端程式處於 LISTEN 狀態,等待客戶端連線請求。
  2. 客戶端向伺服器發出連線請求,請求中首部同步位 SYN = 1,同時選擇一個初始序號 sequence ,簡寫 seq = x。SYN 報文段不允許攜帶資料,只消耗一個序號。此時,客戶端進入 SYN-SEND 狀態。
  3. 伺服器收到客戶端連線後,,需要確認客戶端的報文段。在確認報文段中,把 SYN 和 ACK 位都置為 1 。確認號是 ack = x + 1,同時也為自己選擇一個初始序號 seq = y。請注意,這個報文段也不能攜帶資料,但同樣要消耗掉一個序號。此時,TCP 伺服器進入 SYN-RECEIVED(同步收到) 狀態。
  4. 客戶端在收到伺服器發出的響應後,還需要給出確認連線。確認連線中的 ACK 置為 1 ,序號為 seq = x + 1,確認號為 ack = y + 1。TCP 規定,這個報文段可以攜帶資料也可以不攜帶資料,如果不攜帶資料,那麼下一個資料包文段的序號仍是 seq = x + 1。這時,客戶端進入 ESTABLISHED (已連線) 狀態
  5. 伺服器收到客戶的確認後,也進入 ESTABLISHED 狀態。

這樣三次握手建立連線的階段就完成了,雙方可以直接通訊了。

瀏覽器傳送 HTTP 請求到 web 伺服器

一旦 TCP 連線建立完成後,就開始直接傳輸資料辦正事了!此時瀏覽器會傳送 GET 請求,要求目標伺服器提供 maps.google.com 的網頁,如果你填寫的是表單,則發起的是 POST 請求,在 HTTP 中,GET 請求和 POST 請求是最常見的兩種請求,基本上佔據了所有 HTTP 請求的九成以上。

除了請求型別外,HTTP 請求還包含很多很多資訊,最常見的有 Host、Connection 、User-agent、Accept-language 等

首先 Host 表示的是物件所在的主機。Connection: close 表示的是瀏覽器需要告訴伺服器使用的是非持久連線。它要求伺服器在傳送完響應的物件後就關閉連線。User-agent: 這是請求頭用來告訴 Web 伺服器,瀏覽器使用的型別是 Mozilla/5.0,即 Firefox 瀏覽器。Accept-language 告訴 Web 伺服器,瀏覽器想要得到物件的法語版本,前提是伺服器需要支援法語型別,否則將會傳送伺服器的預設版本。下面我們針對主要的實體欄位進行介紹(具體的可以參考 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers MDN 官網學習)

HTTP 的請求標頭分為四種: 通用標頭請求標頭響應標頭實體標頭

這四種標頭又分別有很多內容,如果你想要深入理解一下關於 HTTP 請求頭的相關內容,可以參考 cxuan 的這篇文章

深入理解 HTTP 標頭

伺服器處理請求併發回一個響應

這個伺服器包含一個 Web 伺服器,也就是 Apache 伺服器,伺服器會從瀏覽器接收請求並將其傳遞給請求處理程式並生成響應。

請求處理程式也是一個程式,它一般是用 .net 、php、ruby 等語言編寫,用於讀取請求,檢查請求內容,cookie,必要時更新伺服器上的資訊的這麼一個程式。它會以特定的格式比如 JSON、XML、HTML 組合響應。

伺服器傳送回一個 HTTP 響應

伺服器響應包含你請求的網頁以及狀態程式碼,壓縮型別(Content-Encoding),如何快取頁面(Cache-Control),要設定的 cookie,隱私資訊等。

比如下面就是一個響應體

關於深入理解 HTTP 請求和響應,可以參考這篇文章

看完這篇HTTP,跟面試官扯皮就沒問題了

瀏覽器顯示 HTML 的相關內容

瀏覽器會分階段顯示 HTML 內容。 首先,它將渲染裸露的 HTML 骨架。 然後它將檢查 HTML 標記併傳送 GET 請求以獲取網頁上的其他元素,例如影像,CSS 樣式表,JavaScript 檔案等。這些靜態檔案由瀏覽器快取,因此你再次訪問該頁面時,不用重新再請求一次。最後,您會看到 maps.google.com 顯示的內容出現在你的瀏覽器中。

我自己肝了六本 PDF,微信搜尋「程式設計師cxuan」關注公眾號後,在後臺回覆 cxuan ,領取全部 PDF,這些 PDF 如下

六本 PDF 連結

相關文章