一、應用層
? ? ? ? 應用層是程序員最關心的一層,需要自定義數據傳輸的格式,即前(客戶端)后(服務器)端交互的接口,然后調用傳輸層的 socket api 來實現網絡通信。
? ? ? ? 自定義數據傳輸的協議,主要是定義兩個部分:
- 通信的內容。
- 通信的數據格式。
1、通信的內容
? ? ? ? 通信的內容根據用戶需求確定。一般用戶不會表達自己的需求,就需要產品經理來溝通和定制。比如一個外賣程序的主頁,需要顯示你附近的商家列表,它的內容就可以是:
請求:
- 用戶信息(根據用戶喜好顯示商家)
- 位置信息(顯示附近的商家)
響應:商家列表。
每個商家的數據:
- 商家的名稱
- 商家的圖片
- 商家的評分
- 商家的簡介
- 商家的距離
- …………
2、通信的數據格式
- 行文本:將所有數據排成一行。比如 qq 發送一條信息包含的內容有 發送用戶 id、接收用戶 id、發送時間、發送內容。但這種方法可讀性很差,以前常用,現在就很少用了。
- xml:通過標簽,對數據進行解釋說明。這種方法雖然可讀性好,但是引入了很多標簽,占用的網絡帶寬更多了,而網絡帶寬是服務器中最貴的資源。所以這種方法也很少用于網絡通信了,更多的是作為本地的配置文件。
- JSON:當前最流行的方式,用 {} 作為界限、用 ; 分隔鍵值對、用 : 分割鍵和值。適用于不是特別缺帶寬的場景。
- google protobuffer:會將傳輸的數據按一定的規則編碼成二進制的比特位,然后進行傳輸。這種方式可讀性非常差,但是消耗的網絡帶寬最少,適用于特別缺帶寬的場景。
- 還有一些現成的應用層協議,可以直接使用,在后續會重點學習 HTTP。
二、傳輸層
? ? ? ? 我們雖然不需要實現傳輸層的代碼,但是需要調用傳輸層提供的 api 。因此我們需要了解傳輸層協議的數據格式,才能更好地使用傳輸層的 api。
1、UDP 協議
? ? ? ? 端口號就不用多說了,他就是 2 個字節的無符號整數,0~65535,不能超過這個范圍,也盡量不要設置?0-1023 知名端口號,它們是專供一些常用的應用層協議使用的。
? ? ? ? UDP 長度也是 2 個字節,即 64 KB,這就意味著一個 UDP 數據報能存儲的數據長度有限制。就比如搜索服務器投放廣告,在 30 年前,廣告很少也很簡單,并且計算機內存也就 1 MB,那時候 64 KB 就很充足。但是現在,廣告越來越多和復雜,不僅有文字還有很多圖片,計算機內存通常也是 16 GB、32 GB 等,64 KB 就遠遠不夠用了。對于這個問題,有幾個設想方案:
- 在應用層對數據進行拆分傳輸。行不通,因為開發量很大,數據拆分涉及到的問題很復雜。部分數據丟包了如何處理;某部分數據出錯如何處理;發送和接收的順序不一致時該如何處理………
- 換成 TCP 協議,它以字節為傳輸的基本單位,沒有明確的上限。
- 升級 UDP 協議,擴充長度為 4 個字節。行不通,協議標準雖然好升級,但是協議的實現是由操作系統開發商廠家們決定的,如果升級出了問題損失的廠家。并且還必須讓所有設備都進行升級,如果一個是新版,一個是舊版,也是無法通信的。所以 UDP 升級的成本很大,用一個新的協議替代 UDP 是成本最低的。
? ? ? ? 校驗和用于校驗數據報在傳輸中是否出錯。數據本質上是通過電信號、光信號、電磁波以高低電平、光電磁波頻率來表述 0、1 比特組成的數據。在環境中可能會收到磁場等環境的影響,隨機概率性地讓高頻 1 變為了低頻 0,或者從 0 變為 1。這種出錯無法完全避免,只能辨別數據是否出錯。校驗和就是一種解決方案:發送方將數據報中的每個字節0、1進行累加為 16 位的整數然后填充到 UDP 報頭的校驗和字段;接收方收到數據報后,按同樣的方法計算一遍校驗和,看是否跟數據報中的校驗和相等,不相等就表示出錯了,直接丟掉數據。當讓也會存在某一個 0 變為 1,某一個 1 變為 0,雖然出錯了但是校驗和沒變的情況,但發生的概率很小。如果在對準確性特別敏感的場景,就需要更精準的其它算法。比如海明碼,他能鑒別哪一位出錯并自動糾錯,但算法更加復雜,代價更大。
? ? ? ? UDP 的應用場景:
? ? ? ? 最常見的是 DHCP:把電腦插到路由器上,該協議會自動給電腦分配一個 IP 地址,而不需要手動配置。還有就是 DNS,會在后續學習。更多的場景是應用在分布式系統中,服務器間的通信,因為服務器都在同個機房,網絡環境簡單,不容易丟包;UDP 的傳輸效率也比較高適合分布式場景。
2、TCP 協議
TCP 協議段格式:
- 源端口號、目的端口號:告知發收雙方設備運行的是哪個程序。
- 首部長度:說明 TCP 報頭的長度。4 位可以代表 0~15,每個數的單位都是 4 個字節,那么首部長度可以代表 0~60 字節。圖中前 5 行每行占 4 字節,那么共占 20 字節。而選項可有可沒有,剩下的字節長度就是選項的長度,占 0~40 字節。
- 保留:UCP 很難實現升級,但又受到 64KB 的限制。保留位就可以在以后升級需要的時候用,同時也不會變動其他部分的格式。
- 校驗和:與 UDP 一致。
2.1、可靠傳輸
? ? ? ? 我們實現不了傳輸的數據 100% 發送給對方,因為就算軟件層面做到了對各種情況的處理,但硬件出了問題軟件層面是沒有辦法的。因此,可靠傳輸指的是:能知道對方是否收到我發送的數據。如果沒有收到(丟包),會做出一些措施補救。
2.1.1、確認應答?
? ? ? ? 接收方收到數據后,會回應一個 “應答報文” (acknowledge, ack)來表明收到數據。比如下面的場景中,女神回復我后,我才知道女神收到了消息,否則就認為消息 “丟包” 了。
? ? ? ? 網絡中也有可能發生 “后發先至” 的情況,因為數據報傳輸的路線不同的,有的交換機/路由器 “擁堵”,有的 “暢通”。如下面的場景,我誤認為女神答應做我的女朋友。
? ? ? ? 序號就是1、2;確認序號就是針對1、針對2。它們都是以字節為單位進行編號的。因為編號是連續遞增的,所以序號保存的是 TCP 載荷的第一個字節的編號,就可以知道后續每個字節的編號。而確認序號保存的是 TCP 載荷的最后一個字節的下一個字節的編號,用于告訴發送方:1、這之前序號的字節我都收到了。2、你從該確認序號的位置開始繼續發送。TCP 接收數據時,會在操作系統內維護一個 “接收緩沖區”,在緩沖區里根據序號進行重排序,解決了后發先至的問題。
? ? ? ? 確認序號只在應答報文中生效,即關鍵字段中 ACK 為 1。
2.1.2、超時重傳
? ? ? ? 確認應答機制針對的是數據成功發送的情況,通過 ack 告訴發送方已收到。而超時重傳機制,針對的是數據發送失敗的情況(丟包),重新發送的策略。
? ? ? ? 超時可以判斷是否丟包,傳輸數據時,只要超過超時時間,還沒收到?ack 就認為丟包。重傳可以減小丟包的概率。比如丟包的概率是 10%,那么再重新發送一次,丟包的概率就減小到 10% x 10% = 1%。
? ? ? ? 沒有收到 ack 有兩種情況:1、發送數據報丟包。2、應答報文丟包。針對1,重傳沒有問題。針對2,重傳會發生接收方收到多個重復數據報的問題。如果是轉賬場景,本來轉 500,卻轉了 1000。
? ? ? ? 針對這種情況,TCP 做了處理:在接收緩沖區中,會對收到的數據報根據序列號進行去重。
? ? ? ? 超時重傳的超時時間是動態變化的,對于同一個數據報,重傳的次數每增加一次,超時時間就會延長。因為如果再次丟包,表示該網絡傳輸的丟包概率不止 10%,頻繁重傳可能還會加重網絡故障的程度。重傳的次數/時間是有上限的,當達到上限后還是丟包,TCP 就會重置,即復位報文中會將 RST (reset)置1。然后釋放連接,相當于刪除之前保存的對方的信息。
? ? ? ? 網絡上所說 TCP 可靠性是由 “三次握手” 實現的,是錯誤的說法。三次握手是 TCP 建立連接時的策略,連接建立好后就沒有三次握手的事情了。
2.2、連接管理
? ? ? ? TCP 是有連接的,具體表現就是通信雙方會保存對方的信息(IP、端口號)。連接建立的過程就是保存對方信息的過程,由三次握手實現;連接斷開的過程就是刪除對方信息的過程,由四次揮手實現。
2.2.1、三次握手(連接建立)
(1)什么是三次握手
? ? ? ? 三次握手,傳輸的是沒有帶載荷(業務數據)的數據報,通過雙方互相同步(synchronized,通知)、應答的方式建立可靠的連接。
? ? ? ? 關鍵流程:三次握手,必定是客戶端先發起請求,建立連接。
- 客戶端發起一個同步報文(syn):告訴服務器,我要跟你建立連接,請你保存我的信息。
- 服務器返回一個應答報文(ack):回應客戶端:收到,我會保存。
- 服務器也發起一個同步報文(syn):到蘇客戶端,我也要和你建立連接,請你保存我的信息。
- 客戶端也返回一個應答報文(ack):回應服務器:收到,我會保存。
? ? ? ? 事實上,服務器的 ack 和 syn 都是操作系統內核控制的,程序員無法在應用層干。操作系統一次合并傳輸兩個數據,提高效率,因為每次數據傳輸的封裝分用有開銷。
(2)三次握手的意義
- 驗證通信鏈路是否通暢:三次握手是可靠傳輸的前提條件,目的是驗證傳輸的鏈路中是否存在問題,而不是傳輸數據(比如首發地鐵,先跑一次空車,看鐵路是否有異常)。
- 驗證通信雙方的發、收能力是否正常:
- 同步通信雙方的序列號:連接建立過程中,TCP 數據有起始序列號,即傳輸的第一個數據報的載荷的第一個字節的序號(SYN、FIN 占一個序列號),客戶端、服務器都有各自的序列號系統。這個起始序列號是一個隨機值,并且每次連接的起始序列號都很不同,這是為了:客戶端與服務器建立第一次連接,并傳輸數據;然后客戶端快速重啟,與服務器建立第二次連接,并傳輸數據。但是,第一次連接后傳輸的某個數據可能會因為網絡延遲,延后到第二次連接后到達,此時我們應該丟掉該數據。如何辨別該數據是否是第一次連接傳輸的呢?就看它的序列號是否與本次連接的起始序號有很大的差異,如果有,就說明不是本次連接的。
(3)三次握手的 TCP 的狀態
?????????TCP 的狀態很多很復雜,我們只需要了解幾個比較重要的:
- LISTEN:服務器 new ServerSocket 就會進入該狀態,表示對服務器端口的監聽,表示服務器存在。當端口上有客戶端過來,就可以 accept 了。
- ESTABLISHED:服務器、客戶端完成三次握手后,就進入該狀態。表示服務器與客戶端的連接已經建立好了。
- 客戶端 new Socket 觸發三次握手;accept 阻塞等待完成連接;三次握手完成后,accept 返回 Socket 對象。
2.2.2、四次揮手(連接斷開)
(1)什么是四次揮手
? ? ? ? 四次揮手可能是客戶端主動發起的,也可能是服務器主動發起的。
? ? ? ? 關鍵過程:
- 客戶端告訴服務器:我要和你斷開連接,請你刪除我的信息。
- 服務器回應收到。
- 服務器也告訴客戶端:我也要和你斷開連接,請你刪除我的信息。
- 客戶端回應收到。
? ? ? ? 跟三次握手不同的是,四次揮手不完全由操作系統內核控制,而是和應用層代碼也有關系。之所以叫四次揮手,而不是三次揮手,就是因為中間的 FIN 是由應用層程序控制的,中間的 FIN 與 ACK 可能不在同一時刻發生,合并不一定發生。
????????不合并的情況:hasNext 返回 false 后,close 之前,還有其他邏輯,ACK 和 FIN 間隔比較長。
? ? ? ? 合并的情況:間隔時間本身不長,服務器 ACK 還延遲應答。這屬于特殊情況,是一種優化手段,合并的情況肯定效率更高,所以還是叫四次揮手,而不是三次揮手。
(2)四次揮手的意義
? ? ? ? 四次揮手就是為了釋放空間,刪除保存的對方信息。
(3)四次揮手的 TCP 狀態
??
- CLOSE_WAIT:被動斷開連接的一方進入的狀態。收到對方發來的 FIN,回應 ACK 的同時,進入該狀態,表示等待 close。這個狀態持續時間很短,如果發現有大量連接都處于此狀態,說明大概率存在文件泄露。
- TIME_WAIT:主動斷開連接的一方進入的狀態。收到對方發來的 FIN,回應 ACK 的同時,進入該狀態,表示等待連接徹底釋放。因為回應的 ACK 可能會丟包,那么對方就會超時重傳 FIN,如果主動方回應 ACK 后立即釋放連接,那么重傳的數據就無人處理。所以會等待 2*MSL 的時間,MSL 是一個充裕的時間,比如 Linux 上是 60s,能夠給予充分的時間完成一趟 FIN 或 ACK 傳輸。
? ? ? ? 三次握手、四次揮手的過程,大部分工作由操作系統內核完成,應用層代碼涉及的部分很少。但是我們還是需要學習,因為有助于理解、調試程序。
2.3、提高傳輸效率
2.3.1、滑動窗口
(1)什么是滑動窗口
? ? ? ? 雖然確認應答、超時重傳機制保證了 TCP 的可靠性,但同時也降低了傳輸效率:每發送一條數據,就需要等待回應一條 ack 報文。
? ? ? ? 滑動窗口機制能緩解傳輸效率降低的問題。首先,批量發送滑動窗口大小個數的一批數據,然后在等待 ack 回應。等到一個 ack,滑動窗口就向后滑動一格并發送一條數據。(之所以沒有等待一批?ack? 后再發送一批數據,是因為 ack 之間是有一個先后順序的,對應了發送數據的先后順序。如果直接等待一批,那么先后順序就沒有了,也就不知道回應的是哪一條數據。)
????????就相當于還沒有等完滑動窗口內的數據的 ack,就提前發送了下一條數據。如下圖中,接收到 2001 的 ack 就認為 1001-2000 已收到,立即發送 5001-6000。窗口內都是等待 ack 的數據,當收到 ack 的速度很快,那么窗口就滑動起來了。
(2)滑動窗口下,應對丟包的處理
- ack 丟包:
? ? ? ? 不做任何處理。雖然 1~1000、2001~3000、3001~4000 的 ack 丟包了,但是 1001~2000 的 ack 能表示 2001 之前的數據都已經收到,4001~5000 的 ack 能表示 5001 之前的數據都已經收到。之后的 ack 能替代之前的 ack。若是最后一個 ack 丟包,那么就會觸發超時重傳機制。
- 數據包丟包:
? ? ? ? 快速重傳。如圖為例子,若 1001~2000 丟包,那么 B 就會反復向 A 索要 1001。當 A 感知到 B 多次索要后,就會重傳 1001~2000。而此時 B 的接收緩沖區已經有了 1~ 7000,那么會繼續索要 7001,后續照常執行。
? ? ? ? 其實,滑動窗口、快速重傳機制 與 確認應答、超時重傳機制是共存的。當 TCP 的傳輸數據量大時,就啟用滑動窗口、快速重傳機制。當 TCP 的傳輸數據量小時,就啟用確認應答、超時重傳機制。
2.3.2、流量控制
? ? ? ? 流量控制,控制的是發送方窗口大小。如果窗口過大,發送速度快,而接受速度跟不上,就容易丟包,又得重傳。因此我們需要根據接收方的接收速度,來調整發送方的窗口大小。接收速度,就是從接收緩沖區 read 的速度,那如何衡量 read 的速度呢?就是接收緩沖區剩余的容量,容量多就表明 read 速度快。接收方返回的 ack 報文中的 16 位窗口大小(若不是 ack 報文,窗口大小字段無意義)位置會存儲接收緩沖區容量,返回給發送方,據此控制滑動窗口大小。但 16 位并不意味著發送方的滑動窗口最多只能控制在 64 kb 的大小。因為選項中有一個項是窗口擴展因子:滑動窗口大小 = 16 窗口大小 << 窗口擴展因子。這意味著 2 的指數級增長,因為左移一位就代表 x 2 倍。
? ? ? ? 補充:read 時會先在接收緩沖區中按序號排好數據,再 read。比如 1001~2000 應在 2001~3000 之前被接收。但 2001~3000 先到達,此時接收緩沖區就會阻塞,等到 1001~2000 入隊后再 read。
????????如下圖為例,演示 A 控制滑動窗口的過程:一開始 B 收到數據后不消費,存儲在接收緩沖區中,最后接收緩沖區容量為 0,A 收到窗口大小為 0 的 ack 就會停止發送數據(因為緩沖區已經沒容量了,繼續發送只會丟包)。當緩沖區有容量后,B 會主動發送窗口更新通知,告知可以繼續傳輸數據。在 A 未收到窗口更新通知的這段時間里,A 會周期性地發送不帶業務數據的窗口探測包,避免因窗口更新通知丟包,而導致 A 一直保持等待的狀態。
2.3.3、擁塞控制
? ? ? ? 實際上傳輸的路徑處于復雜的網絡中,因此滑動窗口的小大不僅要考慮接收方的速度,還要考慮整條傳輸路經上每個節點的速度。擁塞控制的的核心思想就是“嘗試”:如果丟包,說明路徑中右節點的速度更不上,就減小窗口大小,減小速度。如果沒丟包,就適當增大速度。
? ? ? ? 下圖展示了擁塞控制的滑動窗口大小的變化過程:
- 一開始慢啟動,窗口大小很小,處于試探狀態。
- 若沒有丟包,就指數增長,快速增大窗口大小。
- 增長到一個閾值后,就線性增長,增長速率放慢。
- 增長到一定程度,觸發丟包:
- 舊版本(已廢棄):直接回到慢啟動的狀態,然后再指數增長,但閾值減小,再線性增長。
- 為了使 TCP 的傳輸效率更穩定,新版本的做法是:回到新閾值位置,再線性增長。
? ? ? ? 而滑動窗口的最終大小,由流量控制的窗口(發送方維護)和擁塞控制的窗口(接收方返回 ack 通知)共同決定,取較小值。
2.3.4、延時應答
? ? ? ? 為了提高傳輸效率,我們應在保證可靠性的基礎上,增大滑動窗口大小。如何增大滑動窗口大小:輔助手段是,等待一段時間后應答,讓接收方(消費者)消費更多的數據,留出更多的剩余接收緩沖區(前提是等待的這段時間里,發送方沒有持續發送很多數據包)。這不僅能增大滑動窗口,還能減少 ack 的個數(后續的 ack 可以反應前面序號的數據都已經收到,所以可以減少),從而節省帶寬(數據的傳輸需要封裝分用)。
? ? ? ? 但并不是所有的包都能延時應答:
- 當發送的數據量多時,隔一定的數據包個數應答一次。
- 當發送的數據量少時,隔一定的時間應答一次(起碼間隔時間不能超過超時重傳的超時時間)。
2.3.5、捎帶應答
? ? ? ? 客戶端發送請求,服務器接收到請求后會立即返回 ack。而服務器發送響應是在進行一段計算后執行的。ack 僅含報頭(ack 為 1、攜帶確認序號、窗口大小),而數據包相反,含載荷(ack 為 0,確認序號、窗口大小位無效)。當延遲應答發生,返回的數據包剛好能捎帶 ack 的報頭信息,讓兩次傳輸合并為一次,提高傳輸效率。
2.4、面向字節流
? ? ? ? 發送方連續發送幾個數據包時,接收方分用去掉報頭,將載荷放入接收緩沖區,當接收方應用層 read 時,就難以判斷一個數據包載荷的邊界在哪,數據包黏在了一起,即 “粘包問題”:
? ? ? ? 解決粘包問題,有以下方法:
- 以特定的分隔符結尾。
在之前的 TCP 網絡通信編程中,我們是以 '\n' 為分隔符的。
但注意正文中不能有分隔符,不然會錯誤分割。我們可以用 ascii 碼中的不可見字符,來作為分隔符,因為里面很多字符現在都不怎么用了:
- 在每個載荷的開頭存放一個 2 字節的數字,表示數據包載荷的長度。read 時,首先讀取 2 字節,根據該數字來決定讀取幾個字節作為一個數據包。
? ? ? ? 同樣,字符流也會存在粘包問題,比如向文本文件寫入結構化數據,就可以將每個結構化數據寫成一行,再以換行符分隔。而 UDP 不存在這樣的問題,因為它每次發送、傳輸的是一個 DatagramPacket,對應了一個完整的應用層數據包。
? ? ? ? 實際開發中,常基于知名的框架/庫進行開發,這些框架/庫已經在底層處理好粘包問題。但有時需要做 “專用” 的東西,就需要 diy 處理。
2.5、異常情況
- 進程崩潰:此時進程退出,PCB 銷毀,那么文件描述符表也會隨之銷毀,那么 socket 打開的網卡文件也隨之銷毀,隨即發送 FIN。但此時連接還沒被斷開,因為需要完成四次揮手后才完全斷開。
- 主機關機:關機也會導致進程退出,如果關機關得慢,連接斷開的過程與進程崩潰一樣。如果關機關得快,發送 FIN 后還沒有等到 ack,就已經關機了。此時對端也發送 FIN,就沒有 ack 回應,隨后觸發超時重傳,達到上限次數后,就會發送重置報文,單方斷開連接,把對方的信息刪除。而對方也因關機,刪除了內存中保存的自己的信息。
- 主機斷電:斷電是瞬間的事,連 FIN 都沒發出。如果斷電方是接收方,發送方繼續發送數據,而沒有 ack ,然后超時重傳,然后重置斷開連接;如果斷電方是發送方,那接收方就不知道是發送方暫時沒有發數據,還是出現了異常,就會一直處于阻塞等待的狀態。為了避免上述情況,接收方和發送方會互傳 “心跳包”,來感知對方是否存在。當接收方發送心跳包,發送方沒有回應,就表明發送方已斷開連接。這個機制在分布式系統中非常重要,當請求量大時,會讓幾臺機器一起處理,當網關服務器感知到某一臺機器掛了,就會把請求均分給剩下的 3 臺服務器,讓整體的服務更穩定。TCP 協議雖然提供了心跳,但周期較長;有些場景我們需要更靈敏的感知,需要更短的周期,我們就要在應用層寫代碼實現。
- 網線斷開:跟主機斷電的連接斷開操作一樣。
2.6、TCP 報頭的其它字段
- URG:“緊急指針”有效,16 位緊急指針字段里存儲的數字,代表從當前位置往后多少個字節的位置的數據先傳輸。比如 1~1000 的數據包,緊急指針是 1000,那么就先傳輸 1001~2000 的數據包。
- ACK:應答報文。
- PSH:催促接收方,盡快讀取接收緩沖區中的數據。
- RST:復位報文。(單方面斷開連接)
- SYN:同步報文。
- FIN:結束報文。
? ? ? ? 網絡通信大部分情況優先考慮 TCP;機房內部傳輸,不太容易丟包的環境,比如分布式系統,使用效率更高的 UDP;不要求嚴謹的可靠,但要求效率比 TCP 高,就會使用其它的協議。
三、網絡層
1、IP 協議
? ? ? ? 網絡層的 IP?協議主要負責:
- 地址管理:用 IP 地址描述網絡上各個設備的的位置。
- 路由選擇:挑選出合適的傳輸路徑。
????????IP 協議報文結構:
- 4 位版本:ipv4、ipv6。可能還有其他版本,但是沒有推廣使用。
- 4 位首部長度:IP 協議中,報頭變長(可能有選項)。0~15,每個數表示 4 字節,可表示 60 字節。選項最大占 40 字節。
- 8 位服務類型:其中 4 位無效(3 位優先權字段已棄用,1 位保留字段必須置為 0 不用管)。剩余 4 位表示 4 種狀態,不可同時存在,只能選一個。分別表示:最小延時(發送、接收數據的時間盡量短)、最大吞吐量(單位時間內可傳輸的數據盡量多)、最高可靠性(在 IP 層面盡量避開繁忙的節點,減小丟包的概率)、最小成本(消耗的系統資源最少)。但是實際開發中不會設置。
- 16 位總長度:IP 報頭+載荷,最大表示 64 KB。當數據總長度超過 64 KB,IP 協議支持拆包/組包。過程如下:
? ? ? ? 將整個 IP 數據包分成幾部分,放到幾個 IP 數據包的載荷中:
????????用到以下字段:
- 16 位標識:這幾個 IP 數據包的 16 位標識相同,表示拆自同一個 IP 數據包。
- 3 位標志:一位不用;一位表示是否觸發了拆包;一位表示該 IP 數據包是否是最后一個數據包(類似于鏈表中的空引用)。
- 13 位片偏移:描述每個數據包的相對順序。(有可能會發生先發后至的情況)
- 8 位生存時間:該 IP 數據包最多能在網絡上經過幾個路由器轉發。每經過一次,TTL-1。當 TTL 為 0,則表示無法到達,丟棄。TTL 的默認值一般是 32/64/128。例如:初始 TTL 為 64,經過 64 - 46 = 18 次路由器轉發到達搜狗。
? ? ? ? 追蹤中間的轉發節點有哪些:
? ? ? ? 為什么要設置 TTL 生存時間:IP 數據包的轉發必要目的 IP,若 目的 IP 錯誤,或者目的 IP 對應的主機突然下線,數據包就會在網絡中無限轉發。?
- 8 位協議:表示協議類型,用于區分網絡層的載荷交給傳輸層的哪個協議來處理(UDP/TCP)。? PS:對于傳輸層的載荷,用 “目的端口” 來區分交給應用層的哪個應用程序來處理。
- 16 位首部校驗和:網絡層只校驗首部。載荷部分的傳輸層數據由傳輸層的 UDP/TCP 校驗。
- 32 位源/目的 IP 地址:用于標識源/目的設備的位置。
2、地址管理
2.1、IPv4 地址資源有限
? ? ? ? 下圖是?IPv4 地址的點分十進制表示(便于人識別),每個部分占一個字節,取值 0~255。
但是計算機直接識別 32 位 0/1 組成的整數,可表示 0 ~?2^32-1(42億多),在以前這是足夠的,但在移動互聯網時代,每個人都有一部或多部手機,IPv4 地址已經不夠用了。??
2.2、解決方案
- 動態分配 IP 地址:上網的主機才分配地址,不上網的主機就不分配。在早期可行,后續 IP 地址資源進一步緊張,單純使用這個方案,不可行。
- NAT 機制(網絡地址映射):不再是每個設備都有一個唯一的 IP 地址,而是允許不同局域網內有相同的 IP 地址。
- 升級為 IPv6 地址:NAT 只是提升了 IP 地址的利用效率,但沒有從根本上解決問題。IPv4 使用 4 個字節表示 IP 地址;IPv6 使用 16 個字節表示 IPv6 地址。相當于 42 億的四次方的 IP 地址范圍,可讓地球上的每一粒沙子分配一個 IP 地址(電子設備的核心元素“硅”是從沙子中提取出來的)。
? ? ? ? IPv6 在全球的普及度低(IPv4 和 IPv6 不兼容,需要更換路由器設備,成本太高),但在中國普及度達 80% 多。中國不計成本,讓三大運營商升級路由器、讓各大互聯網公司升級應用支持 IPv6 協議,這樣做的原因是為了把 “互聯網土地資源” 分配的權力掌握在自己手上(IPv4 協議體系由美國制定,且控制權大。美國與伊拉克的戰爭中,曾讓伊拉克全國斷網 3 年,讓伊拉克消失在互聯網中)。可觀看紀錄片了解更詳細的歷史:電子監聽、全國斷網,棱鏡門背后,中國如何從末路狂奔到世界之巔_嗶哩嗶哩_bilibili
雖然在中國 IPv6 的使用率仍然很低,但是我們已經做好了設施準備,就算未來某時被某國以控制?IPv4 的使用作為威脅,也不再害怕。
2.3、NAT (網絡地址轉換)
2.3.1、IP 地址分類
所有的 IP 地址分為兩大類:
- 內網/私網 IP:在同一個局域網內唯一。
以三種格式開頭:
a) 10.*
b) 172.16-172.31.*
c) 192.168.*? ?(家用路由器常見格式)
- 外網/公網 IP:在整個網絡上唯一。以其余格式開頭的 IP 地址。
2.3.2、網絡轉發情況
現有 A、B 兩設備:
a) A、B 在同一局域網中:同一局域網中設備的 IP 地址唯一,直接轉發。
b) A、B 在不同局域網中:不可直接相互轉發。
c) A 是內網 IP,B 是外網 IP:A 可主動訪問 B;B 不可主動訪問 A(不同局域網內的 IP 地址可能相同,B 無法辨別是哪個局域網中的 IP),但 A 主動訪問過 B 后,B 可以原路返回。
d) A、B 都是外網 IP:可不涉及?NAT 機制,直接轉發。
原本一個外網 IP 代表一個設備,現在就是一個外網 IP 代表很多個局域網內的所有設備了。
2.3.3、NAPT(網絡地址端口轉換)
? ? ? ? 那么在返回請求的路線中,運營商路由器,如何分辨同一個局域網內的不同內網 IP 呢?端口不僅可以區分一臺主機上的不同程序,還能區分不同主機上的不同程序。因此引入端口輔助映射,區分同一局域網內,不同內網 IP。
PS:也能實現外網設備主動訪問內網設備,那就是內網穿透技術,在應用層實現:
2.3.4、NAT 帶來的好處
? ? ? ? NAT 機制不僅可以緩解 IP 地址資源緊缺的問題,還能加強普通用戶的網絡安全性:因為外網設備無法主動訪問內網設備,所以我們只要不去主動訪問有病毒的網站,就不會被入侵。
2.4、網段劃分
? ? ? ? 一個 IP 地址分為兩個部分:網絡號和主機號。比如 192.168.2.1,192.168.2 表示網絡號,1 表示主機號。
? ? ? ? 同一個局域網中,網絡號必須相同,主機號必須不相同;相鄰的局域網(由路由器連接),網絡號必須不同。如果不滿足以上要求,則無法正常上網。家用路由器的 LAN 口 IP 習慣上設置主機號為 1;路由器會為接入 LAN 口的設備自動分配主機號(DHCP 功能,基于 UDP 應用層協議)。光貓也是路由器,只不過有一個口插光纖,它可以轉換光信號和電信號。下圖是我家網路的簡單示意圖:
? ? ? ? 家用路由器的 IP,通常是前 3 個字節屬于網絡號,但企業/學校/組織機構的路由器就不一定是這種格式了。那么網段劃分的通用方法是什么?子網掩碼:使用 ipconfig 查看主機的網絡信息,每一組對應一個網絡接口(網卡/虛擬網卡,又分為有線/無線網卡)。將子網掩碼轉為二進制,左邊都是 1,右邊都是 0,1 對應的 IP 部分就是網絡號。家用路由器的子網掩碼一般是 255.255.255.0,因為家庭的設備比較少,主機號范圍 0~ 255 足夠用了;如果是企業級的設備很多,就需要減少左邊 1 的個數了。?默認網關就是我的電腦連接的路由器的 LAN IP 地址(網絡的出入口)。
2.5、三種特殊的 IP 地址
- 網絡地址(主機號全0):表示網段本身,不能分配給任何設備。形如:192.168.1.0。
- 廣播地址(主機號全1):發送到這個地址的數據包將轉發給該網絡中的所有設備。形如:192.168.255。廣播地址由網絡層的 IP 協議所支持,只能基于 UDP 協議使用(UDP 是無連接的,TCP 是有連接的,發送方需與該局域網內的所有設備建立連接才可實現,顯然困難得多)。廣播地址,要區別于微信群聊,群聊是在應用層寫程序實現,可以基于 UDP/TCP(把消息發送給服務器,服務器獲取成員列表循環依次轉發給成員)。應用場景:手機投屏。手機不知道電視的 IP,但可以將數據包轉發給廣播地址,廣播地址轉發給局域網內所有設備,安裝了投屏軟件的設備就會做出響應,如電視。然后電視做出響應,告知手機我的 IP 地址。最后手機就將要播放的視頻地址發送給手機 IP。
- 回環地址(127.*):通過該地址發送的數據,能通過該地址接收。通常使用 127.0.0.1,在 Windows 系統中的別名為 localhost。
2.6、固定的劃分方式
? ? ? ? 子網掩碼是現代的網絡劃分方式,在教科書上還有一種過時的固定劃分方式,他將網絡劃分分為 5 類(需要簡單記憶):
A、B 類的主機號范圍都很大,現實世界的局域網用不了這么多設備,IP 地址浪費嚴重。
3、路由選擇
? ? ? ? 路由選擇就是從起點到終點,規劃一條合適的路徑。路由器無法獲得整個網絡的全局信息,只能獲得相鄰設備的情況,因此路由器的路由選擇是探索式的:每個路由器都會維護一個“路由表”,是目的 IP 網絡號、網絡接口(WAN/LAN)的映射關系表,將目的 IP 發送給該路由器,它就會在路由表里查詢是否包含目的 IP。如果包含,則直接轉發給 WAN/LAN;如果不包含,就轉發給默認的路徑,即下一跳(next hop)對應的設備,通常是更高一級的路由器。下面舉一個例子,從我家發送數據到目的 IP 是重慶市渝北區某個服務器的 IP:
四、數據鏈路層
? ? ? ? 負責相鄰兩個節點間的數據傳輸。常見協議:
- 以太網(IEEE 802.3):通過網線/光纖傳輸。
- Wifi(IEEE 802.11):通過無線信號傳輸。
1、以太網幀格式
- 目的/源地址:指的 mac/物理地址,有 6 個字節,范圍比 IPv6 小但比 IPv4 大很多,可以唯一標識設備,在出廠時寫死在網卡中,有些軟件綁定設備就是用的 mac 地址。使用 ipconfig /all 查詢網絡的所有信息:
網絡層的 IP 地址用于路線規劃(描述整個路程起點、終點),數據傳輸層的 mac 地址用于具體執行(描述每個子區間的起點、終點):
- 類型:載荷交給哪個協議處理。有三種值,以十六進制表示:IP、ARP、RARP。
ARP 協議報文:A 向 B 轉發數據,不僅要知道 B 的 IP,還要知道 B 的 mac。在網絡層,路由器需要構建路由表,存儲目的 IP 和 WAN/LAN 接口的映射關系。在數據鏈路層,交換機需要構建轉發表,存儲目的 mac 和具體網絡端口的映射關系。ARP 協議報文協助構建轉發表,可根據 IP 地址獲取對應設備的 mac 地址:因為局域網中的網絡設備隨時可能下線、上限,因此 ARP 報文會周期性觸發進行更新,ARP 廣播給局域網中所有設備,請求每個設備的 mac 地址,它們收到 ARP 請求后進行響應,包含自己的 mac 地址。
- 數據:最大字節 1500 只針對以太網,取決于硬件結構,其他協議上限可能不同。這個上限稱為最大傳輸單元(MTU)。1500 字節是一個非常小的值,相當于 1KB 多點,因此 IP 數據包的載荷用不了 64 KB,它的長度在數據鏈路層就已經限制了,需要拆包。
- CRC:一種校驗和算法。
五、DNS
? ? ? ? 全稱域名解析系統,為應用層協議提供域名解析支持。一個域名對應一個 IP 地址,因為 IP 地址不好記,就用具有實際意義的單詞來代替。
使用 DNS 的另一個好處:更換機器(IP 地址),只需要更新 DNS 系統中的域名和 IP 地址的映射關系即可,對用戶來說沒有變化。
? ? ? ? 早期的 DNS 系統,通過 hosts 文件表示,但隨著網站數目變多,手動更新 hosts 文件頻繁(網站上線/消亡),就非常麻煩了。
C:\Windows\System32\drivers\etc\hosts:一般都是空的,#是注釋。
? ? ? ? 現在的 DNS 系統通過一組 DNS 服務器完成域名解析,但隨著全世界的請求越來越多,開始尋求其它緩解方法:
- 緩存域名解析結果,大幅降低訪問 DNS 服務器的頻率:當首次訪問域名,會向 DNS 服務器查詢 IP 地址,然后操作系統、瀏覽器會保存該映射關系。在緩存未過期的一段時間內,再次訪問同樣的域名,就可以在緩存中直接獲取 IP 地址。
- 分布式系統,引入更多的硬件資源:
? ? ? ? 當瀏覽器打不開網站時,大概率是 DNS 服務器掛了,那么就可以在電腦上配置,更換 DNS 服務器 IP 地址(可以用谷歌的 DNS 服務器 8.8.8.8):
查看網絡連接-》WLAN-》右鍵屬性-》雙擊 Internet 協議版本 4(TCP/IPv4)-》將自動改為自己設置: