完整的一次 HTTP 請求響應過程(二)

YangAM發表於2019-03-02

上篇文章 我們完整的描述了計算機五層模型中的『應用層』和『運輸層』,闡述了較為複雜的 TCP 協議的相關原理,相信大家一定也有所收穫,那麼本篇將繼續五層模型的學習。

網路層

『網路層』其實解決的就是一個「轉發」的問題,通過傳說中的『IP 協議』劃分了網路範圍,即我沒有直接用網線和你連在一起,我也能通過你的 IP 分析出該怎麼樣找到負責你的閘道器路由器,並通過你的閘道器路由給你傳輸資料包。

這就是『網路層』做的事情,它本質上解決了兩臺不存在於同一子網路下的主機相互通訊的問題。而『IP 協議』以及「如何解析 IP 的演算法」算是兩個最核心的內容,我們首先看看這個『IP 協議』的相關概念。

以 IPv4 為例,使用 32 個位元位描述一個 IP 地址,所以理論上,整個 IPv4 可以提供 40 幾個億的 IP 地址,我們一般使用『點分十進位制』來表示。

例如:11000001 00100000 11011000 00001001 的 IP 地址一般記為 193.32.216.9。

由此,我們解決了 IP 編址的問題,但是如何通過 IP 地址判斷出它所屬的子網路呢?

引入一個名詞『子網掩碼』,它在形式上和 IP 地址一樣,使用 32 位位元位進行表述。其中,描述網路部分的位元位全為 1,子網路中的該主機編號部分全為 0 。

例如:子網掩碼 11111111.11111111.11111111.00000000,寫成十進位制就是255.255.255.0。它明確了某個使用該子網掩碼的 IP 的前 24 位是它的子網路部分,而後 8 位是該 IP 對應的主機在子網路下的一個編號。

舉個例子:

IP 地址 172.16.254.1 所對應的子網掩碼為 255.255.255.0,那麼我們只需要做『AND』運算這兩者即可得到該 IP 地址的網路部分。

所以,這個 IP 地址的網路號為 172.16.254 。

下面我們探討一個十分重要的協議,它解決了一個剛加入子網路的主機如何獲取屬於它的 IP 地址的問題,這個協議叫,動態主機配置協議(DHCP)

DHCP

一般來說,我們有兩種方式來配置主機的 IP 地址,一種是管理員手動的指定一個 IP 地址,當然,這樣的成本是非常高的,你不能配置了一個已經被分配出去的 IP地址,即管理員需要記錄所有已分配的 IP 地址。

另外一種呢,就是我們的 DHCP 協議,它允許新加入的主機自動獲取一個 IP 地址以及相關的子網掩碼和閘道器地址等。

預設情況下,路由器隔離廣播包,不會將收到的廣播包從一個子網傳送到另一個子網。當 DHCP 伺服器和客戶端不在同一個子網時,充當客戶端預設閘道器的路由器將廣播包傳送到DHCP伺服器所在的子網,這一功能就稱為 DHCP 中繼(DHCP Relay)。

也就是說,一個子網路中應當有一臺 DHCP 伺服器,用於整個子網中 IP 地址的分配。但為每個子網都單獨配置一個 DHCP 伺服器也有點「愚蠢」。

所以另一種解決辦法就是,某個網路中的閘道器會知道負責該網路的 DHCP 伺服器在什麼位置,IP 地址是什麼,閘道器路由會負責轉發 DHCP 報文請求並返回響應的報文,這就叫 DHCP 中繼。

當然了,實際上現在的路由器本身就可以充當一個 DHCP 伺服器,為其所在的子網提供動態地址獲取服務,所以往往也不需要轉發那樣麻煩。

而完整的 DHCP 請求與響應的過程則是這樣的:

第一步:

DHCP 伺服器發現。 這個階段的首要任務是,找到當前網路中 DHCP 伺服器的位置,並且整個 DHCP 報文的交換是基於 UDP/IP 協議的,向目的埠 67 傳送。

本機由於沒有 IP 地址,所以 IP 資料包中的『源地址』為「0.0.0.0」,『目的地址』為「255.255.255.255」。

這樣在鏈路層廣播該資料包的時候,同一子網路下的所有主機都會接受該資料包,但只有 DHCP 伺服器會響應這個請求。

於是如果路由器本身就是一個 DHCP 伺服器的話,那將進入第二步,否則路由器將分組轉發到 DHCP 伺服器所在的網路內。

第二步:

DHCP 伺服器提供。 DHCP 伺服器,無論是位於外網或是閘道器路由本身,在收到一個『發現報文』後,將響應一個『提供報文』。

該報文中將包含,推薦客戶使用的 IP 地址、子網掩碼、IP 地址租用週期等資訊。

第三步:

DHCP 請求。這其實是一個選擇階段,客戶端主機確認伺服器推薦的引數,決定使用,於是依然以廣播的形式傳送請求向伺服器確認。

第四步:

DHCP ACK。收到客戶端主機發來的確認請求後,伺服器將實際從 IP 池中分配出一塊 IP 地址出來,並返回客戶端確認資訊的 ACK。

從此之後,該主機算是獲得了一塊可用的 IP 地址了,終於加入了網路。

除此之外,還有一個細節不知道大家日常有沒有留心,就是我們對於同一個子網路,IP 地址基本總是一樣的,並沒有因為每次開機後連入網路而被分配不同的 IP。

這一點算是 DHCP 協議的一個約定了,當某臺主機第一次加入某個子網路,它將從 DHCP 伺服器獲取一個全新的 IP 地址。

而以後該主機重新加入到該網路時,將直接進入 DHCP 請求的第三步,將主機上次使用的引數發給伺服器,確認是否可用。而一般情況下伺服器會同意並按照你的要求分配出去一塊 IP 地址,這也是為什麼你每次使用的幾乎是同一 IP。

講完了 DHCP 動態獲取 IP 地址,接著我們簡單看看 IP 資料包的基本格式,並在最後討論一下路由器的選擇演算法,看看一個 IP 資料包是如何被路由器給轉發出去的。

image

關於其中的各個欄位或選項是如何被使用的,我們這裡暫時先不做討論,強行解釋並適合大家理解,等到具體分析報文分發與解析時會容易理解很多。

路由器

路由器是網路層的一個核心裝置,它完成了從「目的 IP 地址」到「目的 IP 所在的子網路」的完整路徑轉發過程。它的內部結構如下:

image

每個埠都直接連線了一臺裝置,而其中的『路由選擇處理器』則負責解析一個輸入埠進來的資料應該被推出到哪個輸出埠中去。

所以,你應該也發現了,整個路由器的核心應該是這個『路由選擇處理器』,也就是驅動這個『路由選擇處理器』工作的演算法,我們稱之為『路由選擇演算法』。演算法本質上就是解決,一個資料包輸入進路由器記憶體,該從哪個輸出口轉發出去的問題。

一個好的 『路由選擇演算法』不僅僅應該解決如何到達目的地的問題,還應該考慮如何最快的到達目的地,即能夠判斷並選擇性的繞過擁塞的網路路徑。

整個路由選擇演算法分為兩大類,全域性式路由選擇演算法和分散式路由選擇演算法。前者的一個最典型的實現就是『鏈路狀態路由選擇演算法』,後者的一個最典型的實現就是『距離向量演算法』。

這兩者演算法的理論原理這裡不再和大家一起探討了,我們著重看看因特網中是如何基於這兩種演算法實現的路由選擇。

首先,整個因特網是一個很龐大且複雜的系統,所以整體上被劃分為一個一個的自治系統(AS),在每一個 AS 中都執行著同樣的路由演算法,自治系統之間使用 BGP 協議交換資訊。

image

整個因特網大致就是這樣的一個個自治系統互聯構成的,而自治系統內部的所有路由器都執行著同樣的路由選擇演算法,基於距離向量的『RIP 協議』或基於鏈路狀態的『OSPF 協議』。

至於為什麼要拆分自治系統,等我們介紹完這個 RIP 或者 OSPF,你就明白了。

RIP 協議的演算法是這樣的

image

簡單的一個自治系統,我們以此為例看看整個 RIP 協議是如何工作的。

首先最開始,路由器 A 的轉發表肯定是這樣的:

----------------------------
目的子網   下一跳路由   跳數
x           B           1
q           E           1
----------------------------
複製程式碼

其他路由器也是類似的,第一步都建立起與自己直接相連鄰居的連線。

第二步是一個不斷進行的過程,相鄰的路由器之間每隔 30 秒就相互交換資訊,告知對方自己的轉發表內容。

所以經過一次交換之後,路由器 A 將收到來自 B 和 E 的轉發表資訊,於是路由轉發表更新如下:

----------------------------
目的子網   下一跳路由   跳數
x           B           1
q           E           1
y           B           2
p           E           2
----------------------------
複製程式碼

但是這裡有一個細節,子網路 y 是可以通過 A - B - y 到達的,但同時也可以通過 A - E - C - y 到達。你也許已經猜到了,路由器當然會選擇最短路徑的一條來更新自己的轉發表。

所以,這個距離向量的演算法本質上就是通過相互之間不斷的交換資訊以保證某個自治系統內,所有的路由器都知道某個目的子網的最短路徑。

OSPF 的實現是這樣的:

我們同樣以上面的例子進行解釋:

image

OSPF 是基於鏈路狀態路由選擇演算法進行實現的,所以它也是一個全域性性路由選擇演算法,演算法執行一次即可完成全網的路由資訊更新。

而 OSPF 本質上就是一個迪傑斯特拉求最短路徑問題,它通過不斷的迭代與計算更新整個路由轉發表。假設現在我們的路由器 A 執行 OSPF 協議:

第一次迭代完成後,它得到與 B、E 兩臺路由器相關的子網路的路徑計算。

第二次以 B 或者 E 為起點重新執行演算法,這裡我們假設以 B 為起點執行了演算法,那麼與 C 相關的子網路的路徑也被更新進 A 的路由轉發表。

第三次以 C 為起點同樣的執行演算法,得到和 D 相關的子網路路徑更新。

由於 D 作為末端路由,並沒有直接相連的其他路由,所以演算法不再繼續,回到 E。

第四次,以 E 作為原點,執行演算法,得到了 C 相關子網路的路徑,如果有更短的路徑,將更新 A 的轉發表以最優路徑。

那麼,待整個演算法執行結束,一個自治系統中的所有路由器幾乎全部遍歷,但是卻不同於 RIP,OSPF 相對而言收斂快,可以迅速完成任務,而 RIP 則需要不斷的交換資訊以達到需求,往往會陷入一個長週期。

當然了,OSPF 需要較強的 CPU 計算能力和更多的記憶體儲存空間。所以總的而言,他們都廣泛應用於整個因特網之中,RIP 應用在較為底層的 ISP 上,而 OSPF 則執行在較為高階的 ISP 中。

至此,整個網路層的基本內容也介紹完了,總結一下,網路層的核心任務就是負責轉發分組,而如何將分組轉發到目的主機的網路中呢,牽扯出 IP 協議,通過 IP 地址與子網掩碼劃分子網路,而路由器執行路由選擇演算法得知目的子網路的完整路由路徑並進行分發。

鏈路層

網路層解決的是,分組轉發的目的網路,也就是轉發給目的網路的閘道器路由,而鏈路層解決的是,將分組廣播給個人,也即目的主機。

網路層的 IP 資料包會在鏈路層被封裝成『乙太網幀』,它的基本結構是這樣的:

image

前導碼用於同步時鐘,按照我的理解就是區分一個一個的幀,源和目的地址指的是『Mac 地址』,也稱作實體地址。

『Mac 地址』是硬體級別的主機唯一標識,由生產廠家唯一確定。類似這樣:

34-E6-AD-17-A5-6B

全球任意一臺主機的 Mac 地址都是不同的,它不像 IP 地址可以在別人不用的時候共享。

下面我們要講一個協議,它完成了主機 IP 地址到 Mac 地址的轉換,他就是 ARP 地址解析協議。

ARP 協議其實有點類似於我們之間在應用層介紹的 DNS 協議,輸入一個域名 地址,輸出一個 IP 地址,而 ARP 而言,輸入一個 IP 地址,輸出一個 Mac 地址。

網路中的每臺主機,包括路由器,都內建的 ARP 模組和 ARP 表。當一份資料包到達鏈路層時,首先要做的就是以該資料包的目的 IP 作為輸入,先查詢自己主機的 ARP 模組,如果能夠得到該 IP 的目的主機 Mac 地址,那麼封裝一個乙太網幀交給物理層傳送出去就好。

而如果本機的 ARP 表中並沒有儲存目的 IP 主機的 Mac 地址,那麼就需要向同網路中的其他主機進行查詢。

傳送方會構建一個特殊的 ARP 分組,源 Mac 地址為傳送方的 Mac 地址,目的 Mac 地址為廣播地址:255.255.255.255,以及源和目的 IP 地址,本質上就是一個特殊的乙太網幀。

於是該網路下的所有主機都將收到這個 ARP 分組,那麼他們要做的就是拆開 IP 地址比對是否和自己的 IP 地址相同,如果是則響應一個 ARP 分組,告訴傳送方自己的 Mac 地址。

如果不是自己,則還會檢查自己的 ARP 模組,看看是否能提供幫助。

最終,傳送方會得到想要的目的 Mac 地址並更新自己的 ARP 表,然後封裝一個正常的乙太網幀傳送出去。

由於乙太網採取的是『廣播』方式,所以同一子網路中任意一臺主機傳送報文,所有的其餘主機都會收到,但是它們會匹配目的 Mac 地址是否是自己,不是則丟棄,這一點很重要。

好了,那麼到此為止我們也簡單介紹了鏈路層的相關內容,關於物理層,其實沒什麼介紹的,就是 0、1 的電訊號傳輸。

關於整個 OSI 五層模型,我們從上至下也已經完成了學習,下一篇將完整的看看 「www.baidu.com」之後,整個計算機網路發生的故事,其實有點標題黨了,最後一篇才介紹完整的 HTTP 請求過程,見諒!


文章中的所有程式碼、圖片、檔案都雲端儲存在我的 GitHub 上:

(https://github.com/SingleYam/overview_java)

歡迎關注微信公眾號:撲在程式碼上的高爾基,所有文章都將同步在公眾號上。

image

相關文章