IP 協議
IP 協議全稱為 “網際互連協議(Internet Protocol)”,IP 協議是 TCP/IP 體系中的網絡層協議。
基本概念
網絡層解決的問題
TCP 作為傳輸層控制協議,其保證的是數據傳輸的可靠性和傳輸效率,但 TCP 提供的僅僅是數據傳輸的策略,而真正負責數據在網絡中傳輸的則傳輸層之下的網絡層和鏈路層。
雙方在進行網絡通信時,發送的數據并不是直接從一方的傳輸層直接發送到了另一方的傳輸層,而是需要傳輸層將數據繼續向下進行交付,在網絡層和鏈路層經過數據封裝后再通過網絡發送到對方主機,對方主機收到數據后也同樣需要在鏈路層和網絡層進行數據解包,此時對方的傳輸層才拿到了發送過來的數據,然后再繼續將該數據向上進行交付。
網絡通信的過程,就像兩個人在送互相送數據,這兩個人分別在兩棟樓的四樓,如果一個人要將數據交給對方,那么這個人就必須先從四樓走到一樓,然后再在路上經過路徑選擇到達對方樓下,最后再上到四樓將數據交給對方。
其中,送數據的這個人從四樓下來的過程就是數據封裝的過程,這個人在路上經過路徑選擇到達對方樓下的過程就是數據路由的過程,而這個人再上到四樓將數據交給對方的過程就是數據解包的過程。
而網絡層要解決的問題就是,將數據從一臺主機送到另一臺主機,也就是數據的路由。
保證數據可靠的從一臺主機送到另一臺主機的前提
當雙方在進行基于 TCP 的網絡通信時,要保證將數據可靠的從一臺主機送到另一臺主機,前提是發送方要有將數據送到對方主機的能力,要是發送方連將數據發送給對方的能力都沒有,那就更不用談可靠的將數據送給對方主機了。
需要注意的是,發送方有將數據送到對方主機的能力,并不意味著發送方每次發送的數據都能夠成功的發送到對方,但如果發送方連將數據發送給對方的能力都沒有,那發送方基本就不可能將數據發送給對方。
一旦發送方有了將數據發送給對方的能力,就算發送方某次發送的數據沒有成功到達對方,此時上層 TCP 由于沒有收到對應數據的應答,此時上層 TCP 會要求進行數據重發,直到數據成功發送到對方主機為止。
也就是說,在網絡層有能力將數據送到對方主機的情況下,雖然網絡層不能保證每次都能將數據成功送到對方主機,但在 TCP 提供的可靠性策略的保證下,最終網絡層就一定能夠將數據可靠的發送到對方主機。
說明一下:
網絡層解決的問題是,將數據從一臺主機送到另一臺主機,因此網絡層解決的是主機到主機的問題。
一方傳輸層從上方進程拿到數據后,該數據貫穿網絡協議棧進行封裝和解包,最終到達對方傳輸層,此時對方傳輸層也會將數據向上交給對應的進程,因此傳輸層解決的是進程到進程的問題。
路徑選擇
數據進行的網絡傳輸一般都是跨網絡的,而路由器就是連接多個網絡的硬件設備,因此數據在進行跨網絡傳輸時一定需要經過多個路由器。
數據路由就像我們旅游一樣,當確定了要到達的目標主機后,就需要尋找最短的路徑到達該目的地。
目的地的確定是非常重要的,因為目的地直接決定了數據路由時的路徑選擇,這也是跨網絡找到目標主機的根本。
只有數據經過了較為正確的路徑選擇,最終才可能慢慢趨近于目標網絡或目標主機。
確定數據路由的目的地后,數據就可以在網絡中進行路由了,但數據在路由時無法自行進行路徑選擇,因為這個數據本身是 “不認識路” 的,因此數據在路由的過程中需要不斷 “找路人問路”,而這里所謂的“路人” 就是網絡當中的一臺臺路由器。
網絡當中的路由器是 “認識路的”,它們將自己的“認路經驗” 都記錄到路由表當中,因此路由器可以通過查路由表找到去特定點的最短路徑。因此數據在路由時,會不斷通過路由器來進行路徑選擇,以此來一步步靠近目標網絡或目標主機。
主機和路由器
主機:配有 IP 地址,但是不進行路由控制的設備。但實際現在幾乎不存在不進行路由控制的設備了,就連你的筆記本也會進行路由控制。
路由器:既配有 IP 地址,又能進行路由控制。實際現在主流的路由器已經不僅僅具有路由的功能了,它甚至具備某些應用層的功能。
節點:主機和路由器的統稱。
IP 協議格式
IP 協議格式如下:
4 位版本號(version):指定 IP 協議的版本(IPv4/IPv6),對于 IPv4 來說,就是 4。
4 位首部長度(header length):表示 IP 報頭的長度,以 4 字節為單位。
8 位服務類型(Type Of Service):3 位優先權字段(已經棄用),4 位 TOS 字段,和 1 位保留字段(必須置為 0)。4 位 TOS 分別表示:最小延時,最大吞吐量,最高可靠性,最小成本。這四者相互沖突,只能選擇一個。比如對于 ssh/telnet 這樣的應用程序,最小延時比較重要,而對于 ftp 這樣的程序,最大吞吐量比較重要。
16 位總長度(total length):IP 報文(IP 報頭 + 有效載荷)的總長度,用于將各個 IP 報文進行分離。
16 位標識(id):唯一的標識主機發送的報文,如果數據在 IP 層進行了分片,那么每一個分片對應的 id 都是相同的。
3 位標志字段:第一位保留,表示暫時沒有規定該字段的意義。第二位表示禁止分片,表示如果報文長度超過 MTU,IP 模塊就會丟棄該報文。第三位表示 “更多分片”,如果報文沒有進行分片,則該字段設置為 0,如果報文進行了分片,則除了最后一個分片報文設置為 0 以外,其余分片報文均設置為 1。
13 位片偏移(framegament offset):分片相對于原始數據開始處的偏移,表示當前分片在原數據中的偏移位置,實際偏移的字節數是這個值× 8 \times 8×8 得到的。因此除了最后一個報文之外,其他報文的長度必須是 8 的整數倍,否則報文就不連續了。
8 位生存時間(Time To Live,TTL):數據報到達目的地的最大報文跳數,一般是 64,每經過一個路由,TTL -= 1,一直減到 0 還沒到達,那么就丟棄了,這個字段主要是用來防止出現路由循環。
8 位協議:表示上層協議的類型。
16 位首部檢驗和:使用 CRC 進行校驗,來鑒別數據報的首部是否損壞,但不檢驗數據部分。
32 位源 IP 地址和 32 位目的 IP 地址:表示發送端和接收端所對應的 IP 地址。
選項字段:不定長,最多 40 字節。
IP 報頭在內核當中本質就是一個位段類型,給數據封裝 IP 報頭時,實際上就是用該位段類型定義一個變量,然后填充 IP 報頭當中的各個屬性字段,最后將這個 IP 報頭拷貝到數據的首部,至此便完成了 IP 報頭的封裝。
IP 如何將報頭與有效載荷進行分離?
IP 分離報頭與有效載荷的方法與 TCP 是一模一樣的,當 IP 從底層獲取到一個報文后,雖然 IP 不知道報頭的具體長度,但 IP 報文的前 20 個字節是 IP 的基本報頭,并且這 20 字節當中涵蓋 4 位首部長度。
因此 IP 是這樣分離報頭與有效載荷的:
當 IP 從底層獲取到一個報文后,首先讀取報文的前 20 個字節,并從中提取出 4 位的首部長度,此時便獲得了 IP 報頭的大小s i z e sizesize。
如果s i z e sizesize 的值大于 20 字節,則需要繼續從報文當中讀取s i z e − 20 size-20size−20 字節的數據,這部分數據就是 IP 報頭當中的選項字段。
讀取完 IP 的基本報頭和選項字段后,剩下的就是有效載荷了。
IP 就是通過這種 “定長報頭 + 自描述字段” 的方式進行報頭和有效載荷的分離的。但需要注意的是,IP 報頭當中的 4 位首部長度描述的基本單位與 TCP 報頭當中的 4 位首部長度一樣,都是以 4 字節為單位進行描述的,這也恰好是報文的寬度。
4 位二進制的取值范圍是 0000 ~ 1111,因此 IP 報頭的最大長度為15 × 4 = 60 15\times 4=6015×4=60 字節,因為基本報頭的長度是 20 字節,所以 IP 報頭中選項字段的長度最多是 40 字節。如果 IP 報頭當中不攜帶選項字段,那么 IP 報頭的長度就是 20 字節,此時報頭當中的 4 位首部長度字段所填的值就是20 ÷ 4 = 5 20\div 4=520÷4=5,即 0101。
IP 如何決定將有效載荷交付給上層的哪一個協議?
基于 IP 協議的傳輸層協議不止一種,因此當 IP 從底層獲取到一個報文并對其進行解包后,IP 需要知道應該將分離后得到的有效載荷交付給上層的哪一個協議。
在 IP 報頭當中有一個字段叫做 8 位協議,該字段表示的就是上層協議的類型,IP 就是根據該字段判定應該將分離出來的有效載荷交付給上層的哪一個協議的。該字段是發送方的 IP 層從上層傳輸層獲取到數據后填充的,比如是上層 TCP 交給 IP 層的數據,那么該數據在封裝 IP 報頭時的 8 位協議填充的就是 TCP 對應的編號。
32 位源 IP 地址和 32 位目的 IP 地址
IP 報頭當中的 32 位源 IP 地址和 32 位目的 IP 地址,分別代表的就是該報文的發送端和接收端對應的 IP 地址。
數據在網絡傳輸過程中會遇到一個個的路由器,這些路由器會幫助網絡當中的數據進行路由轉發,使得網絡中的數據慢慢趨近于目標主機。路由器在幫助數據進行路由轉發時,會提取出該數據的 IP 報頭當中的目的 IP 地址,并以此作為數據路由轉發的重要依據。
當接收端收到了發送端發來的數據后,接收端可能也想要給發送端發送數據,因此發送端在發送數據時除了需要指明該數據的目的 IP 地址,還需要指明該數據的源 IP 地址,也就是發送端的 IP 地址。即便接收端收到數據后沒有數據想要發送給發送端,但至少接收端需要向發送端發送一個響應報文,表明發送端發送的數據已經被接收端可靠的收到了,因此發送出去的數據除了需要指明該數據的目的 IP 地址,還需要指明該數據的源 IP 地址。
理解 socket 編程:
在進行 socket 編程的時候,當一端想要發送數據給另一端時,必須要指明對端的 IP 地址和端口號,也就是發送數據的目的 IP 地址和目的端口號。
其中這里的 IP 地址就是給網絡層的 IP 用的,用于數據在網絡傳輸過程中的路由轉發,而這里的端口號就是給傳輸層的 TCP 或 UDP 用的,用于指明該數據應該交給上層的哪一個進程。
發送數據時我們不需要指明發送數據的源 IP 地址和源端口號,因為傳輸層和網絡層都是在操作系統內核當中實現的,數據在進行封裝時操作系統會自行填充上對應的源 IP 地址和源端口號。
8 位生存時間
報文在網絡傳輸過程中,可能因為某些原因導致報文無法到達目標主機,比如報文在路由時出現了環路路由的情況,或者目標主機已經異常離線了,此時這個報文就成了一個廢棄的游離報文。
為了避免網絡當中出現大量的游離報文,于是在 IP 的報頭當中就出現了一個字段,叫做 8 位生存時間(Time To Live,TTL)。8 位生存時間代表的是報文到達目的地的最大報文跳數,每當報文經過一次路由,這里的生存時間就會減一,當生存時間減為 0 時該報文就會被自動丟棄,此時這個報文就會在網絡中消散。
分片與組裝
數據鏈路層解決的問題
IP 能夠將數據跨網絡從一臺主機送到另一臺主機,而數據在進行跨網絡傳送時,需要經過一個個的路由器進行路由轉發,最終才能到達目標主機。
比如要將數據從主機 B 跨網絡傳送到主機 C,那么主機 B 需要先將數據交給路由器 F,路由器 F 再將數據交給路由器 G,…,最終由路由器 D 將數據交給主機 C。
因此 IP 進行數據跨網絡傳送的前提是,需要先將數據從一個節點傳送到和自己相連的下一個節點,這個問題實際就是由 IP 之下的數據鏈路層解決的,其中數據鏈路層最典型的代表協議就是 MAC 幀。
而兩個節點直接相連也就意味著這兩個節點是在同一個局域網當中的,因此要討論兩個相鄰節點的數據傳送時,實際討論的就是局域網通信的問題。
最大傳輸單元 MTU
MAC 幀作為數據鏈路層的協議,它會將 IP 傳下來的數據封裝成數據幀,然后發送到網絡當中。但 MAC 幀攜帶的有效載荷的最大長度是有限制的,也就是說 IP 交給 MAC 幀的報文不能超過某個值,這個值就叫做最大傳輸單元(Maximum Transmission Unit,MTU),這個值的大小一般是 1500 字節。
在 Linux 下使用ifconfig命令可以查看對應的 MTU。
由于 MAC 幀無法發送大于 1500 字節的數據,因此 IP 層向下交付的數據的長度不能超過 1500 字節,這里所說的數據包括 IP 的報頭和 IP 的有效載荷。
分片與組裝
如果 IP 層要傳送的數據超過了 1500 字節,那么就需要先在 IP 層對該數據進行分片,然后再將分片后的數據交給下層 MAC 幀進行發送。
如果發送數據時在 IP 層進行了分片,那么當這些分片數據到達對端主機的 IP 層后就需要先進行組裝,然后再將組裝好的數據交付給上層傳輸層。
注意:
數據的分片不是經常需要做的,實際在網絡通信過程中不分片才是常態,因為數據分片會存在一些潛在的問題,比如分片可能會增加丟包的概率。
數據的分片和組裝發生在 IP 層,不僅源端主機可能會對數據進行分片,數據在路由過程中的路由器也可能對數據進行分片。因為不同網絡的 MTU 是不一樣的,如果傳輸路徑上的某個網絡的 MTU 比源端網絡的 MTU 小,那么路由器就可能對 IP 數據報再次進行分片。
分片數據的組裝只會發生在目的端的 IP 層。
在分片的數據中,每一個分片在 IP 層都會被添加上對應的 IP 報頭,而傳輸層添加的報頭只會出現在第一個分片中,因此網絡中傳輸的數據包可能沒有傳輸層的報頭。
數據的分片和組裝都是由 IP 層完成的
數據的分片和組裝都是在 IP 層完成的,上層的傳輸層和下層的鏈路層并不關心。
傳輸層只負責為數據傳送提供可靠性保證,比如當數據傳送失敗后,傳輸層的 TCP 協議可以組織進行數據重傳。
當 TCP 將待發送的數據交給 IP 后,TCP 并不關心該數據是否會在 IP 層進行分片,即 TCP 并不關心數據具體的發送過程。
當 TCP 從 IP 獲取到數據后,TCP 也不關心該數據是否在 IP 層經過了組裝。而鏈路層的 MAC 幀只負責,將數據從一個節點傳送到和自己相連的下一個節點。
當 IP 將待發送的數據交給 MAC 幀后,MAC 幀并不知道該數據是 IP 經過分片后的某個分片數據,還是一個沒有經過分片的數據,MAC 幀只知道它一次最多只能發送 MTU 大小的數據,如果 IP 交給 MAC 幀大于 MTU 字節的數據,那 MAC 幀就無法進行發送。
當 MAC 幀從網絡中獲取到數據后,MAC 幀也不關心這個數據是否需要進行組裝,MAC 幀只需要將該數據的 MAC 幀報頭去掉后直接上交給上層 IP 就行了,而至于該數據的組裝問題則是 IP 需要解決的。
因此,數據的分片和組裝完全是由 IP 協議自己完成的,傳輸層和鏈路層不必關心也不需要關心。
分片的過程
假設 IP 層要發送 4500 字節的數據,由于該數據超過了 MAC 幀規定的 MTU,因此 IP 需要先將該數據進行分片,然后再將一個個的分片交給 MAC 幀進行發送。
IP 報頭如果不攜帶選項字段,那么其大小就是 20 字節,假設 IP 層添加的 IP 報頭的長度就是 20 字節,并按下列方式將數據分片后形成了四個分片報文:
需要注意的是,分片后的每一個分片數據都需要封裝上對應的 IP 報頭,因此 4500 字節的數據至少需要分為四個分片報文進行發送。
分片報文到達對方的 IP 層后需要被重新組裝起來,因此 IP 層在對數據進行分片時需要記錄分片的信息,而 IP 報頭當中的 16 位標識、3 位標志和 13 位片偏移實際就是與數據分片相關的字段。
16 位標識:唯一標識主機發送的報文,如果數據在 IP 層進行了分片,那么每一個分片報文的 16 位標識是相同的。
3 位標志:第一位保留,表示暫時沒有規定該字段的意義。第二位表示禁止分片,表示如果報文長度超過 MTU,IP 模塊就會丟棄該報文。第三位表示 “更多分片”,如果報文沒有進行分片,則該字段設置為 0,如果報文進行了分片,則除了最后一個分片報文設置為 0 以外,其余分片報文均設置為 1。
13 位片偏移:分片相對于原始數據開始處的偏移,表示當前分片在原數據中的偏移位置,實際偏移的字節數是這個值× 8 \times 8×8 得到的。因此除了最后一個報文之外,其他報文的長度必須是 8 的整數倍,否則報文就不連續了。
因此上述四個分片報文對應的 16 位標識都是一樣的,假設四個分片報文的 16 位標識都是 123,則這四個報文對應的 16 位標識、3 位標志中的 “更多分片” 和 13 位片偏移分別如下:
需要注意的是,13 位片偏移當中記錄的字節數是當前分片在原數據開始處的偏移字節數的值÷ 8 \div 8÷8 得到的,比如分片報文 2 在原始數據開始處的偏移字節數是 1480,其對應的 13 位片偏移的值就是1480 ÷ 8 = 185 1480\div 8=1851480÷8=185。
組裝的過程
MAC 幀交給 IP 層的數據可能來自世界各地,這些數據可能是經過分片后發送的,也可能是沒有經過分片直接發送的,因此 IP 必須要通過某種方式來區分收到的各個數據。
IP 報頭當中有 32 位源 IP 地址,源 IP 地址記錄了發送端所對應的 IP 地址,因此通過 IP 報頭當中的 32 位源 IP 地址就可以區分來自不同主機的數據。
IP 報頭當中有 16 位標識,未分片的數據各自的 16 位標識都是不同的,而由同一個數據分片得到的各個分片報文所對應的 16 位標識都是相同的,因此通過 IP 報頭當中 16 位標識就可以判斷哪些報文是沒有經過分片的獨立報文,哪些報文是經過分片后的分片報文。因此 IP 可以通過 IP 報頭當中的 32 位源 IP 地址和 16 位標識,將經過分片的數據各自聚合在一起,聚合在一起后就可以開始進行組裝了。
對于各個分片報文來說:
第一個分片報文中的 13 位片偏移的值一定為 0。
最后一個分片報文中的 “更多分片” 標志位一定為 0。
對于每一個分片報文來說,當前報文的 13 位片偏移加上當前報文的數據字節數 ÷ \div÷ 8 所得到的值,就是下一個分片報文的所對應的 13 位片偏移。
根據分片報文的這三個特點就能夠將分片報文合理的組裝起來。
先找到分片報文中 13 位片偏移為 0 的分片報文,然后提取出其 IP 報頭當中的 16 位總長度字段,通過計算即可得出下一個分片報文所對應的 13 位片偏移,按照此方式依次將各個分片報文拼接起來。
直到拼接到一個 “更多分片” 標志位為 0 的分片報文,此時表明分片報文組裝完畢。
分片報文丟包的問題
分片后的報文在網絡傳輸過程中也可能會出現丟包問題,但接收端有能力判斷是否收到了全部分片報文,比如假設某組分片報文對應的 16 位標識值為 x:
如果分片報文中的第一個分片報文丟包了,那么接收端收到的分片報文中就找不到對應 16 位標識為 x,并且 13 位片偏移為 0 的分片報文。
如果分片報文中的最后一個分片報文丟包了,那么接收端收到的分片報文中就找不到對應 16 為標識為 x,并且 “更多分片” 標志位為 0 的分片報文。
如果分片報文中的其它分片報文丟包了,那么接收端在進行分片報文的組裝時就會找不到對應 13 位片偏移為特定值的分片報文。
需要注意的是,未分片報文的 “更多分片” 標志位為 0,最后一個分片報文的 “更多分片” 標志位也為 0,但當接收端只收到分片報文中的最后一個分片報文時,接收端不會將其識別成一個未分片的報文,因為未分片的報文所對應的 13 位片偏移的值也應該是 0,而最后一個分片報文所對應的 13 位片偏移的值不為 0。
因此只有當一個報文的 13 位片偏移為 0,并且該報文的 “更多分片” 標志位也為 0 時,該報文才會被識別成一個沒有被分片的獨立報文,否則該報文就會被識別成一個分片報文。
為什么不建議進行分片?
雖然傳輸層并不關心 IP 層的分片問題,但分片對傳輸層也是有影響的。
如果一個數據在網絡傳輸過程中沒有經過分片,那么只要接收端收到了這一個報文,我們就可以認為該數據被對方可靠的收到了。
而如果一個數據在網絡傳輸過程中進行了分片,那么只有當接收端收到了全部的分片報文并將其成功組裝起來,這時我們才認為該數據被對方可靠的收到了。但如果眾多的分片報文當中有一個報文出現了丟包,就會導致接收端就無法將報文成功組裝起來,這時接收端會將收到的分片報文全部丟棄,此時傳輸層 TCP 會因為收不到對方應答而進行超時重傳。
假設在網絡傳輸時丟包的概率是萬分之一,如果將數據拆分為一百份進行發送,那么此時丟包的概率就上升到了百分之一。因為只要有一個分片報文丟包了也就等同于這個報文整體丟失了,因此分片會增加傳輸層重傳數據的概率。
需要注意的是,只要分片報文當中的某一個出現了丟包,此時傳輸層都需要將數據整體進行重傳,因為傳輸層并不知道底層 IP 對數據進行了分片,當傳輸層發送出去的數據得不到應答時傳輸層就只能將數據整體進行重傳,因此數據在發送時不建議進行分片。
如何盡可能避免分片?
實際數據分片的根本原因在于傳輸層一次向下交付的數據太多了,導致 IP 無法直接將數據向下交給 MAC 幀,如果傳輸層控制好一次交給 IP 的數據量不要太大,那么數據在 IP 層自然也就不需要進行分片。
因此 TCP 作為傳輸控制協議,它需要控制一次向下交付數據不能超過某一閾值,這個閾值就叫做 MSS(Maximum Segment Size,最大報文段長度)。
通信雙方在建立 TCP 連接時,除了需要協商自身窗口大小等概念之外,還會協商后續通信時每一個報文段所能承載的最大報文段長度 MSS。
MAC 幀的有效載荷最大為 MTU,TCP 的有效載荷最大為 MSS,由于 TCP 和 IP 常規情況下報頭的長度都是 20 字節,因此一般情況下 MSS = MTU - 20 - 20,而 MTU 的值一般是 1500 字節,因此 MSS 的值一般就是 1460 字節。
所以一般建議 TCP 將發送的數據控制在 1460 字節以內,此時就能夠降低數據分片的可能性。之所以說是降低數據分片的可能性,是因為每個網絡的鏈路層對應的 MTU 可能是不同的,如果數據在傳輸過程中進入到了一個 MTU 較小的網絡,那么該數據仍然可能需要在路由器中進行分片。
網段劃分
IP 地址的構成
IP 地址由網絡號和主機號兩部分構成:
網絡號:保證相互連接的兩個網段具有不同的標識。
主機號:同一網段內,主機之間具有相同的網絡號,但是必須有不同的主機號。可以在 IP 地址的后面加一個 /,并在 / 后面加上一個數字,這就表示從頭數到第幾位為止屬于網絡標識。
例如,下圖中路由器連接了兩個網段。對于網絡標識來講,同一網段內主機的網絡標識是相同的,不同網段內主機的網絡標識是不同的。而對于主機標識來講,同一網段內主機的主機標識是不同的,不同網段內主機的主機標識是可以相同的。
不同的子網其實就是把網絡號相同的主機放到一起。
如果在子網中新增一臺主機,則這臺主機的網絡號和這個子網的網絡號一致,但是主機號必須不能和子網中的其他主機重復。
DHCP 協議
實際手動管理 IP 地址是一個非常麻煩的事情,當子網中新增主機時需要給其分配一個 IP 地址,當子網當中有主機斷開網絡時又需要將其 IP 地址進行回收,便于分配給后續新增的主機使用。
因此對于 IP 地址的分配和回收一般不會手動進行,而是采用 DHCP(Dynamic Host Configuration Protocol,動態主機配置協議)技術。
DHCP 通常被應用在大型的局域網環境中,其主要作用就是集中地址管理、分配 IP 地址,使網絡環境中的主機動態獲得 IP 地址、Gateway 地址、DNS 服務器地址等信息,并能夠提升地址的使用率。
DHCP 是一個基于 UDP 的應用層協議,一般的路由器都帶有 DHCP 功能,因此路由器也可以看作一個 DHCP 服務器。
當我們連接 WiFi 時需要輸入密碼,本質就是因為路由器需要驗證你的賬號和密碼,如果驗證通過,那么路由器就會給你動態分配了一個 IP 地址,然后你就可以基于這個 IP 地址進行各種上網動作了。
先找目標網絡,再找目標主機
當 IP 要將數據跨網絡從一臺主機發送到另一臺主機時,其實不是直接將數據發送到了目標主機,而是先將數據發送到目標主機所在的網絡,然后再將數據發送到目標主機。
因此數據在路由時的第一目的并不是找到目標主機,而是找到目標網絡所在的網絡,然后再在目標網絡當中找到目標主機。
數據路由時之所以不一開始就以找目標主機為目的,因為這樣效率太低了。
找主機的過程本質是排除的過程,如果一開始就以找目標主機為目的,那么在查找的過程中一次只能排除一個主機。
而如果一開始先以找目標網絡為目的,那么在查找過程中就能一次排除大量和目標主機不在同一網段的主機,這樣就可以大大提高檢索的效率。
因此,為了提高數據路由的效率,我們對網絡進行了網段劃分。
網段劃分
過去曾經提出一種劃分網絡號和主機號的方案,就是把所有 IP 地址分為五類,如下圖所示:
因此,各類 IP 地址的取值范圍如下:
A 類:0.0.0.0 到 127.255.255.255。
B 類:128.0.0.0 到 191.255.255.255。
C 類:192.0.0.0 到 223.255.255.255。
D 類:224.0.0.0 到 239.255.255.255。
E 類:240.0.0.0 到 247.255.255.255。當要判斷一個 IP 地址是屬于哪一類時,只需要遍歷 IP 地址的前五個比特位,第幾個比特位最先出現 0 值,那么這個 IP 地址對應就屬于 A、B、C、D、E 類地址。
子網劃分
但隨著網絡的飛速發展,這種劃分方案的局限性很快就顯現出來了。
比如一些學校、公司、實驗室等組織想要申請自己的局域網,由于 A 類地址的網絡號只占 7 個比特位,因此 A 類地址可申請的網絡只有2 7 2^727 個,于是大多數組織都選擇申請 B 類地址。
由于 B 類地址的主機號占 16 個比特位,因此理論上一個 B 類網絡當中允許有 65536 臺主機。
但實際網絡架設中,一般不會存在一個局域網當中有這么多主機的情況,也就意味著大量的 IP 地址實際都被浪費掉了。
為了避免這種情況,于是又提出了新的劃分方案,稱為 CIDR(Classless Interdomain Routing):
在原有的五類網絡的基礎上繼續進行子網劃分,這也就意味著需要借用主機號當中的若干位來充當網絡號,此時為了區分 IP 地址中的網絡號和主機號,于是引入了子網掩碼(subnet mask)的概念。
每一個子網都有自己的子網掩碼,子網掩碼實際就是一個 32 位的正整數,通常用一串 “0” 來結尾。
將 IP 地址與當前網絡的子網掩碼進行 “按位與” 操作,就能夠得到當前所在網絡的網絡號。
此時一個網絡就被更細粒度的劃分成了一個個更小的子網,通過不斷的子網劃分,子網中 IP 地址對應的主機號就越來越短,因此子網當中可用 IP 地址的個數也就越來越少,這也就避免了 IP 地址被大量浪費的情況。
比如在某一子網中將 IP 地址的前 24 位作為網絡號,那么該網絡對應的子網掩碼的 32 個比特位中的前 24 位就為 1,剩下的 8 個比特位為 0,將其用點分十機制表示就是 255.255.255.0。
假設該子網當中有一臺主機對應的 IP 地址是 192.168.128.10,那么將這個 IP 地址與該網絡對應的子網掩碼進行 “按位與” 操作后得到的就是 192.168.128.0,這就是這個子網對應的網絡號。
實際在用子網掩碼與子網當中主機的 IP 地址進行 “按位與” 操作時,本質就是保留了主機 IP 地址中前 24 個比特位的原貌,將剩下的 8 個比特位的值清 0 了而已,也就是將主機號清 0 了,所以 “按位與” 后的結果就是該網絡對應的網絡號。
需要注意的是,子網劃分不是只能進行一次,我們可以在劃分出來的子網的基礎上繼續進行子網劃分。
因此一個數據在路由的時候,隨著數據不斷路由進入更小的子網,其網絡號的位數是在不斷變化的,準確來說其網絡號的位數是在不斷增加的,這也就意味著 IP 地址當中的主機號的位數在不斷減少。最終當數據路由到達目標主機所在的網絡時,就可以在該網絡當中找到對應的目標主機并將數據交給該主機,此時該數據的路由也就結束了。
特殊的 IP 地址
并不是所有的 IP 地址都能夠作為主機的 IP 地址,有些 IP 地址本身就是具有特殊用途的。
將 IP 地址中的主機地址全部設為 0,就成為了網絡號,代表這個局域網。
將 IP 地址中的主機地址全部設為 1,就成為了廣播地址,用于給同一個鏈路中相互連接的所有主機發送數據包。
127.* 的 IP 地址用于本機環回(loop back)測試,通常是 127.0.0.1。
也就是說,IP 地址中主機號為全 0 的代表的是當前局域網的網絡號,IP 地址中主機號為全 1 的代表的是廣播地址,這兩個 IP 地址都是不能作為主機的 IP 地址的。因此在某個局域網中最多能存在的主機個數是 2 主機號位數 − 2 2^{主機號位數}-22主機號位數−2。
本機環回基本原理
本機環回會將數據貫穿網絡協議棧,但最終并不會將數據發送到網絡當中,相當于本機環回時不會將數據寫到網卡上面。
本機環回的目的就是將數據自頂向下貫穿協議棧,進行一次數據封裝的過程的過程,然后再自底向上貫穿協議棧,進行一次數據的解包和分用,用于測試本地的網絡功能是否正常。
本機環回的基本原理:
當數據到達 IP 層需要繼續向下交付時,如果是環回程序,那么 IP 輸出函數會將該數據放入到 IP 輸入隊列當中,然后再由 IP 輸入函數讀取上去。
而 IP 輸入函數將數據讀取上去的本應該是鏈路層交付上來的數據,因此該數據后續就會被當作從網絡中讀取上來的數據看待,各層協議會對該數據依次進行解包和分用。
如果不是環回程序的話,那么接下來就會判斷該數據對應的目的 IP 地址是否為廣播或多播地址,或者目的 IP 地址是否與本主機的 IP 地址相同,如果是則也會將該數據放入到 IP 輸入隊列當中,等待 IP 輸入函數將其讀走。
只有判斷程序不是環回程序,并且也不是廣播或多播,或發給本主機的數據后,才會用 ARP 獲取該數據目的主機的以太網地址并進行后續數據發送的操作。
loopback 設備:
IP 地址的數量限制
IP 地址數量不足問題
我們知道,IP 地址(IPv4)是一個 4 字節 32 位的正整數,因此一共有2 32 2^{32}232 個 IP 地址,也就是將近 43 億個 IP 地址。但 TCP/IP 協議規定,每個主機都需要有一個 IP 地址。
現在全世界人口已經有 70 多億了,就算有一半的人沒有智能手機,算下來也有 30 多億臺智能手機需要 IP 地址。
隨著科技的發展,我們使用的電腦、智能手表、智能冰箱、智能洗衣機等設備如果要入網也是需要 IP 地址的。
另外,IP 地址并不是按照主機臺數來配置的,因此一個主機可能需要多個 IP 地址,更別談還有很多組網的路由設備也需要 IP 地址,以及一些特殊的 IP 地址不能使用的問題。所以 43 億個 IP 地址其實早就不夠用了,因此才提出了 CIDR 的方案對已經劃分好的五類網絡繼續進行子網劃分,其目的就是為了減少 IP 地址的浪費,根本原因就是 IP 地址本來就不夠了,所以不能夠再浪費了。
CIDR 雖然在一定程度上緩解了 IP 地址不夠用的問題,因為 CIDR 提高了 IP 地址的利用率,減少了浪費,但 IP 地址的絕對上限并沒有增加。
如何解決 IP 地址不足問題
解決 IP 地址不足有以下幾種方式:
動態分配 IP 地址:只給接入網絡的設備分配 IP 地址,因此同一個 MAC 地址的設備,每次接入互聯網中,得到的 IP 地址不一定是相同的,避免了 IP 地址強綁定于某一臺設備。
NAT 技術:能夠讓不同局域網當中同時存在兩個相同的 IP 地址,NAT 技術不僅能解決 IP 地址不足的問題,而且還能夠有效地避免來自網絡外部的攻擊,隱藏并保護網絡內部的計算機。
IPv6:IPv6 用 16 字節 128 位來表示一個 IP 地址,能夠大大緩解 IP 地址不足的問題。但 IPv6 并不是 IPv4 的簡單升級版,它們是互不相干的兩個協議,彼此并不兼容,因此目前 IPv6 還沒有普及。
私網 IP 地址和公網 IP 地址
私網 IP 地址的種類
如果一個組織內部組建局域網,IP 地址只用于局域網內的通信,而不直接連到 Internet 上,理論上使用任意的 IP 地址都可以,但是 RFC 1918 規定了用于組建局域網的私有 IP 地址。
10.*,前 8 位是網絡號,共 16,777,216 個地址。
172.16.* 到 172.31.*,前 12 位是網絡號,共 1,048,576 個地址。
192.168.*,前 16 位是網絡號,共 65,536 個地址。
包含在這個范圍中的,都稱為私網 IP,其余的則稱為公網 IP(或全局 IP)。
我們連接云服務器時,連接的這個 IP 地址就是云服務器的公網 IP 地址。
我們可以通過ifconfig命令來查看我們這臺機器的私網 IP,其中網絡接口 lo(loop)代表的是本地環回,而 eth0 代表的就是我這臺機器的網絡接口,可以看到我的私網 IP 地址是 172.21.0.15。
需要注意的是,這里連接云服務器時的 IP 地址 49.232.66.206,是云服務器的公網 IP,由于我使用的是騰訊云,因此這里的 172.21.0.15 是我這個云服務器在騰訊內部的私網 IP,可以看到這個 IP 正好在第二種私網 IP 范圍內。
此外,打開 Windows 當中的 cmd 窗口,通過ipconfig命令可以看到大量以 192.168 開頭的私網 IP。
我們為什么要給運營商交錢?
我們享受的是互聯網公司提供服務,但為什么需要向運營商交錢呢?
實際網絡通信的基礎設施都是運營商搭建的,我們訪問服務器的數據并不是直接發送到了對應的服務器,而是需要經過運營商建設的各種基站以及各種路由器,最終數據才能到達對應的服務器。
因為運營商為我們提供了通信的基礎設施,所以我們交網費實際就相當于購買入網許可一樣。
沒有運營商提供的這些基礎設施,就不會誕生所謂的互聯網公司,因為互聯網公司是誕生在網絡通信基礎之上的。
也就是說,用戶上網的數據首先必須經過運營商的相關網絡設備,然后才能發送到互聯網公司對應的服務器。因此所謂的網段劃分、子網劃分等工作實際都是運營商做的。
數據是如何發送到服務器的
路由器是連接兩個或多個網絡的硬件設備,在路由器上有兩種網絡接口,分別是 LAN 口和 WAN 口:
LAN 口(Local Area Network):表示連接本地網絡的端口,主要與家庭網絡中的交換機、集線器或 PC 相連。
WAN 口(Wide Area Network):表示連接廣域網的端口,一般指互聯網。
我們將 LAN 口的 IP 地址叫做 LAN 口 IP,也叫做子網 IP,將 WAN 口的 IP 地址叫做 WAN 口 IPO,也叫做外網 IP。
我們使用的電腦、家用路由器、運營商路由器、廣域網以及我們要訪問的服務器之間的關系大致如下:
不同的路由器,子網 IP 其實都是一樣的(通常都是 192.168.1.1),子網內的主機 IP 地址不能重復,但是子網之間的 IP 地址就可以重復了。
每一個家用路由器,其實又作為運營商路由器的子網中的一個節點,這樣的運營商路由器可能會有很多級,最外層的運營商路由器的 WAN 口 IP 就是一個公網 IP 了。
如果希望我們自己實現的服務器程序,能夠在公網上被訪問到,就需要把程序部署在一臺具有外網 IP 的服務器上,這樣的服務器可以在阿里云 / 騰訊云上進行購買。
由于私網 IP 不能出現在公網當中,因此子網內的主機在和外網進行通信時,路由器會不斷將數據包 IP 首部中的源 IP 地址替換成路由器的 WAN 口 IP,這樣逐級替換,最終數據包中的源 IP 地址成為一個公網 IP,這種技術成為 NAT(Network Address Translation,網絡地址轉換)。
為什么私網 IP 不能出現在公網當中?
不同的局域網中主機的 IP 地址可能是相同的,所以私網 IP 無法唯一標識一臺主機,因此不能讓私網 IP 出現在公網上,因為 IP 地址要能唯一標識公網上的一臺主機。
但由于 IP 地址不足的原因,我們不能讓主機直接使用公網 IP 而讓主機使用私網 IP,因為私網 IP 可以重復也就意味著我們可以在不同的局域網使用相同的 IP 地址,緩解了 IP 的不足。
此外,我們不能直接使用公網 IP 還有一個原因就是,因為我們的數據包必須要經過運營商的路由器,如果我們發送的數據直接到了公網,那也就意味著我們再也不用交網費了,這是不現實的。
兩個局域網當中的主機不能不跨公網進行通信
兩個局域網當中的主機理論上是不能不跨公網進行通信的,因為一個主機要將數據發送給另一臺主機的前提是得先知道另一臺主機的 IP 地址。
即便現在這個主機知道了另一臺主機的 IP 地址,但有可能這兩臺主機的 IP 地址是一樣的,因為它們的 IP 地址都是私網 IP 地址。
當這一臺主機發送數據時將目的 IP 地址填成和自己相同的 IP 地址,操作系統就會認為這個數據就是要發給自己的,而不會向外進行發送了。
所以數據要從一個局域網發送到另一個局域網,如果不經過公網是基本上不可能的。我們在和別人聊天的時候,也不是直接將數據從一個局域網直接發送到了另一個局域網,而是先將數據經過公網發送到了服務器,然后再由服務器將數據經過公網轉發到了另一個局域網。
但實際確實存在一些技術能夠使數據包在發送過程中不進行公網 IP 的替換,而將數據正確送到目標主機,這種技術叫做內網穿透,也叫做 NAT 穿透。
路由
數據 “問路” 的過程
數據在路由的過程中,實際就是一跳一跳(Hop by Hop)“問路”的過程。所謂 “一跳” 就是數據鏈路層中的一個區間,具體在以太網中指從源 MAC 地址到目的 MAC 地址之間的幀傳輸區間。
IP 數據包的傳輸過程中會遇到很多路由器,這些路由器會幫助數據包進行路由轉發,每當數據包遇到一個路由器后,對應路由器都會查看該數據的目的 IP 地址,并告知該數據下一跳應該往哪跳。
路由器的查找結果可能有以下三種:
路由器經過路由表查詢后,得知該數據下一跳應該跳到哪一個子網。
路由器經過路由表查詢后,沒有發現匹配的子網,此時路由器會將該數據轉發給默認路由。
路由器經過路由表查詢后,得知該數據的目標網絡就是當前所在的網絡,此時路由器就會將該數據轉給當前網絡中對應的主機。
路由表查詢的具體過程
每個路由器內部會維護一個路由表,我們可以通過route命令查看云服務器上對應的路由表。
Destination代表的是目的網絡地址。
Gateway代表的是下一跳地址。
Genmask代表的是子網掩碼。
Flags中,U 標志表示此條目有效(可以禁用某些條目)G 標志表示此條目的下一跳地址是某個路由器的地址,沒有 G 標志的條目表示目的網絡地址是與本機接口直接相連的網絡,不必經路由器轉發。
Iface代表的是發送接口。
當 IP 數據包到達路由器時,路由器就會用該數據的目的 IP 地址,依次與路由表中的子網掩碼 Genmask進行 “按位與” 操作,然后將結果與子網掩碼對應的目的網絡地址Destination進行比對,如果匹配則說明該數據包下一跳就應該跳去這個子網,此時就會將該數據包通過對應的發送接口Iface發出。
如果將該數據包的目的 IP 地址與子網掩碼進行 “按位與” 后,沒有找到匹配的目的網絡地址,此時路由器就會將這個數據包發送到默認路由,也就是路由表中目標網絡地址中的default。可以看到默認路由對應的Flags是UG,實際就是將該數據轉給了另一臺路由器,讓該數據在另一臺路由器繼續進行路由。
數據包不斷經過路由器路由后,最終就能到達目標主機所在的目標網絡,此時就不再根據該數據包目的 IP 地址當中的網絡號進行路由了,而是根據目的 IP 地址當中的主機號進行路由,最終根據該數據包對應的主機號就能將數據發送給目標主機了。
路由表生成算法
路由可分為靜態路由和動態路由:
靜態路由:是指由網絡管理員手工配置路由信息。
動態路由:是指路由器能夠通過算法自動建立自己的路由表,并且能夠根據實際情況進行調整。
路由表相關生成算法:距離向量算法、LS 算法、Dijkstra 算法等。