Linux_網絡基礎-傳輸層

1. 端口號

端口號(Port)在一臺機器設備上唯一的標識了一個進程

端口號範圍劃分:

0 - 1023 :知名端口號,有些應用層協議被廣泛的使用,因此就爲這些應用層協議分配了固定的端口號,例如 HTTP-80、HTTPS-443、SSH-22、FTP-21
1024 - 65535 :操作系統動態分配的端口號,客戶端程序的端口號,就是從這個範圍內隨機分配的

一個進程允許同時綁定多個端口號,但是一個端口號不允許被多個進程同時綁定

2. UDP 協議

UDP 協議格式:
在這裏插入圖片描述
UDP長度: 協議報頭和數據部分的整體長度,最大 64K(超出 UDP 一次能發送的最大長度,需要在應用層進行手動分包,然後進行發送,並在應用層手動拼裝)

CRC校驗(循環冗餘校驗碼): 一個變量 uint16_t check_num; 遍歷數據報的每個字節,往 check_num 上加(溢出沒關係),發送端會根據數據內容計算一個校驗和,接收端拿着自己計算好的校驗和和發送端發送來的校驗和進行對比,如果不相同說明數據已經出錯了

md5 : 字符串哈希算法(消息摘要算法)

  1. 不管字符串多長,得到的 hash 結果都是固定長度(64位/128位)

  2. 原串如相同,得到的 hash 值一定相同,原串如果不同(哪怕差別很小),得到的hash的值也會差別很大

  3. 根據原串得到 md5 很簡單,但是根據 md5 推算回原串理論上幾乎不可能

  4. md5sum 命令特別常用,把 md5 當做校驗和來使用

  5. 有可能出現兩個不同的原串,md5 碰巧相同:理論上存在,但是在工程上不考慮

UDP的特點:

  • 無連接:知道對端的 ip 和端口號,就能進行傳輸數據,不需要建立連接

  • 不可靠:沒有確認應答機制,沒有重傳機制,丟包了也不會給應用層返回任何錯誤信息

  • 面向數據報:傳輸不夠靈活,同一份數據不能控制傳輸的次數和大小 ,應用層交給 UDP 多長的報文 UDP 就會原封不用的進行發送

UDP緩衝區:

  • UDP 沒有發送緩衝區,要發送的數據直接交給內核,由內核將數據傳給網絡層進行後續的傳輸動作

  • UDP 存在接受緩衝區,但是這個緩衝區不能保證接收到的數據和發送的數據的順序一致性,並且一旦這個緩衝區滿了,在接受到的數據將會丟失

TCP 協議

TCP 協議格式:
在這裏插入圖片描述
源端口號和目的端口號: 表示數據從哪個進程來的並且要到哪個進程去

32位序號和32位確認號: 見下文

4位首部長度: 表示首部有多少個32位bit位(4個字節),所以 TCP 首部的最大長度爲15*4=60個字節

6個標誌位:
URG:緊急指針是否有效
ACK:確認號是否有效
PSH:提示接收端應用程序立刻將數據從緩衝區中取走
RST:對方要求重新建立連接,通常將攜帶 RST 標識的成爲復位報文段
SYN:請求建立連接,通常將攜帶 SYN 標識的稱爲同步報文段
FIN:通知對方本端要關閉連接,通常將攜帶 FIN 標識的稱爲結束報文段

16位窗口大小: 見下文

16位校驗和: CRC 校驗,md5 消息摘要算法

16位緊急指針: 標記哪一部分是緊急數據

40個字節的頭部選項: 不介紹


確認應答 ACK 機制

在這裏插入圖片描述

TCP 將每個字節的數據都進行了編號(序號)

每一個 ACK 都帶有對應的確認序號,意思是告訴發送者我已經接收到了哪些數據,你應該從哪個位置開始發送

超時重傳機制

在這裏插入圖片描述
如圖所示:因爲網絡擁堵,或者其他原因,發送的數據可能會發生丟包問題,此時發送端接受不到 ACK 應答,隔一段時間後將會對數據進行重傳,而 TCP 會悲觀的面對丟包問題,因此這個超時時間,一次比一次長(Linux下以500ms作爲單位)

除了上圖中看到情況外,還有一種情況接受不到 ACK ,就是在對方發送應答信息的時候發生了丟包,如下圖

在這裏插入圖片描述
在這種情況中雖然發送端沒有收到 ACK,但是接收端確實已經接受到了數據,再次重發數據將會導致發送的數據重複,而序列號可以幫助接受端完成數據去重的工作

這裏的超時時間最好的轉態就是找到一個最小的時間,這個時間可保證 “一定能接收到 ACK”
如果超時時間太長,將會導致傳輸效率遍低,如果超時時間太短,又會造成頻繁發送重複包問題

連接管理機制

建立連接(三次握手):
在這裏插入圖片描述
第一次握手:客戶端向服務端發送連接請求 SYN
第二次握手:服務端回覆 ACK 應答,並且同時發送 SYN 連接請求
第三次握手:客戶端回覆 ACK 應答

建立連接是客戶端和服務端雙方共同的事情,因此只有雙方都同意建立連接,連接纔會成功

斷開連接(四次揮手):
在這裏插入圖片描述
第一次揮手:客戶端調用 close 函數 發送 FIN 結束報文段
第二次揮手:服務端回覆 ACK 應答
第三次揮手:服務端調用 close 函數 發送 FIN 結束報文段
第四次揮手:客戶端進行 ACK 應答

TCP 四次揮手有沒有可能是三次呢??
有,因爲捎帶應答(見下文)

服務器狀態轉換:

  • [CLOSED->LISTEN] 關閉狀態到服務端調用 listen 函數進入監聽狀態,等待客戶端連接
  • [LISTEN->SYN_RECV] 一旦接受到客戶端發送的連接請求(SYN),就會將連接放入內核等待隊列中,並向客戶端發送 SYN
  • [SYN_RECV->ESTABLISHED] 服務端接收到客戶端的 ACK 應答後進入成功連接狀態,此時可以開始傳輸數據
  • [ESTABLISHED->CLOSE_WAIT] 當客戶端主動關閉連接時(調用close函數),服務端收到 FIN 結束報文段,服務端回覆 ACK 之後進入 CLOSE_WAIT 狀態(等待調用close)
  • [CLOSE_WAIT->LAST_ACK] 服務端調用 close 後發送 FIN,此時進入 LAST_ACK 狀態,等待着最後一個 ACK 到來(確認對方收到了FIN)
  • [LAST_ACK->CLOSED] 服務端收到客戶端發來的 ACK,徹底關閉連接

客戶端狀態轉換:

  • [CLOSED->SYN_SENT] 客戶端調用connect,主動向服務端發送 SYN 連接請求
  • [SYN_SENT->ESTABLISHED] 當客戶端收到服務端回覆的 ACK 將會進入成功連接狀態,並回復 ACK 應答
  • [ESTABLISHED->FIN_WAIT1] 客戶端向服務端主動發送斷開連接請求進入 FIN_WAIT1 狀態,等待對端應答
  • [FIN_WAIT1->FIN_WAIT2] 客戶端接收到對端的應答進入 FIN_WAIT2 狀態,等待對端發送的 FIN
  • [FIN_WAIT2->TIME_WAIT] 當客戶端接收到了 FIN,向對端發送 ACK,並進入 TIME_WAIT 狀態等待對方成功接收 ACK

TIEM_WAIT 狀態存在的意義,爲什麼客戶端最後回覆了 ACK 後沒有直接徹底關閉連接,而是進入 TIME_WAIT 狀態呢???
答:因爲最後一個 ACK 應答也是有可能會失敗的,留出一定的時間,如果應答失敗了服務端還有機會重傳 FIN
TIME_WAIT 會保持 2MSL 這樣的時間,MSL 表示網絡中的兩個節點之間,傳輸一個數據所經歷的最大時間,Centos7 MSL 默認爲 60s,可以手動改

滑動窗口

ACK 應答機制,就是每當發送一個數據,都必須接受一次應答,才能發送下一段數據,但是每次都需要應答,這樣未免效率太低了

爲了解決這個問題,滑動窗口一次發送多條數據(不必等待每一個應答),可以實現提高效率(本質上是將多次等待 ACK 的時間重疊在一起)

在這裏插入圖片描述
窗口大小:指的是無需應答就可以發送數據的最大值,上圖中窗口大小爲4000個字節(四個段)

發送前四個段的時候,無需等待 ACK 應答直接發送,

當收到第一個 ACK 應答後窗口向後滑動,發送第五段的數據

操作系統爲了維護滑動窗口,需要建立發送緩衝區來記錄當前哪些數據還沒有接收到應答,只有接收到應答的數據才被認爲發送成功,纔會從發送緩衝區中刪除,沒有應答的數據將會進行重傳

窗口越大,網絡的吞吐率會越高,但是不能無限大

  1. 發送方如果發送的太快,接收方可能處理不過來-流量控制 (見下文)
  2. 如果當前網絡比較擁堵,發送方如果發送的很快仍然會丟包-擁堵控制(見下文)

在這裏插入圖片描述
如果發生的丟包,如何進行重傳??

情況一: 數據包已經被對端接受,但是沒有收到 ACK
在這裏插入圖片描述
如果只是丟了部分包問題不大,如上圖,丟了第三個包的應答,但是接受到了第四個包的應答,這就可以證明,接受端接收到了第三個包,因爲如果接收端沒有接收到第三個包,接收端會一直回覆第二個包的 ACK 應答來請求第三個包(見下圖)

如上圖:發送端收到前兩個包的應答後,窗口會向後滑動2個段,但是收不到第三個包的應答,不會繼續向後滑動,直到收到第四個包後一次向後滑動兩個段

情況二: 發送的數據包直接丟失,對端沒有接收到數據

在這裏插入圖片描述
如上圖,如果接收端沒有接收到 1001-2001 的報文段,就會一直髮送 ACK(下一個是1001),直到發送端接收到了三個相同的 ACK 就會進行重發數據,這時接收端收到重發的 1001-2001 後,就會直接發送 ACK(下一個是9001),因爲前面的數據已經接受到了,被放到了接受緩衝區中

這種方式被稱爲 “快速重發機制” (也叫"快重傳")

但是如果 1001-2001 和 2001-3001 兩個包都丟失了,那麼在接收到重發的 1001-2001 包後,會繼續重複發送 ACK(下一個是2001),直到發送端再次接收三次相同的應答後,重發數據 2001-3001,所以如果發送的是 ACK(下一個是9001) 就能確定前面的數據都已經接收到

流量控制

接收端處理數據的能力是有限的,如果發送端發送的太快接收端來不及處理,數據都被堆積在接受緩衝區中,最終將會導致接收緩衝區堆滿,一旦堆滿在發來的數據都將會發生丟包問題

爲了解決這個問題,發送端的發送速度就不能是越快越好,而是要根據接收端處理數據的能力(根據接收緩衝區剩餘空間大小來判斷處理能力大小)來決定,這種機制就叫做流量控制

  • 接收端會將自己緩衝區的大小放入 TCP 首部的 “窗口大小” 字段,通過 ACK 通知發送端,窗口大小越大說明網絡吞吐量越大
  • 接收端一旦發現自己的緩衝區快滿了就會將 “窗口大小” 字段設置爲一個較小的值,通知發送端
  • 發送得知 “窗口大小” 的值比較小後,會減慢發送的速率
  • 一旦 “窗口大小” 爲0,說明接受緩衝區滿了,停止發送
  • 等待接收端發送窗口更新通知,但是窗口更新通知如果丟包了,就不能正常發送了,因此
  • 發送端會定時發送一個 窗口探測 包,來獲知 “窗口大小” 從而斷定是否開始發送數據

TCP 首部的 “窗口大小” 只有16個字節,最大65535,難道窗口大小就只有16個字節嗎,其實不是的,實際上 TCP 首部的選項中還包含了一個窗口擴大因子M,實際的窗口大小是 “窗口大小” 字段的值左移 M 位

擁塞控制

滑動窗口可以高效的發送大量的數據,但是如果一開始在不知道網絡狀況的情況下就貿然的發送大量的數據依舊可能會出現問題,因此引入慢啓動機制,一開始先發送少量的數據,探探路,在決定以多大的速率來發送數據

擁塞窗口: 每次開始發送時定義擁塞窗口爲1(默認網絡堵塞),每次接收到 ACK 擁塞窗口將會以指數級別增大(每次發送數據包的時候都將擁塞窗口和接收端返回的窗口大小進行對比,較小的作爲實際發送窗口)

慢啓動闕值: 慢啓動雖然一開始很慢但是增長速度很快,指數增長太快了因此有一個闕值,TCP 剛開始啓動的闕值就是窗口的最大值,但是每次超時重發,這個闕值都會減半並且擁塞窗口置1,擁塞窗口指數增長到這個闕值後將變爲線性增長

延遲應答

和滑動窗口密切相關,給接收方一定的時間處理數據,這樣應答返回的空間大小就會更大

假設接收端緩衝區爲1M,一次接收到了 500K 的數據,如果立刻應答,返回的窗口大小就是 500K
但是接收端可能處理數據的能力很強,很短的時間就將數據處理完畢,這時再進行應答那麼返回的窗口就是1M
這種情況下即使窗口在大些,接受端依舊能處理的過來

但是也不是每個包都需要進行延遲應答

  1. 數量限制:每隔 N 個包就應答一次
  2. 時間限制:每當超出最大延遲時間就應答一次

具體的數量和超時時間,依據不同的操作系統也會不同,一般N=2,超時時間爲 200ms

捎帶應答

延遲應答的基礎上,引入捎帶應答機制

很多情況下客戶端給服務端發送了數據,服務端也同樣會給客戶端回覆數據,那麼此時 ACK 就可以搭一個順風車,和回覆的數據一起發送,
在這裏插入圖片描述

面向字節流

創建一個 TCP socket,同時在內核中創建發送緩衝區接收緩衝區

發送數據時會先將數據放到發送緩衝區中,如果發送的數據太多拆分成多個 TCP 包進行發送,如果數據太少,就會先等待其他數據

接收數據時,數據從網卡到達內核的接收緩衝區中,然後程序從接受緩衝區中取數據

對於這樣的情況,既可以讀數據,又可以寫數據,叫做全雙工

正是由於緩衝區的存在,100 個字節的數據我可以選擇一次性全部發送,也可以一次1個字節發送100次,接收的時候也可以一次全部接受,也可以多次接收,所以 TCP 的一個特點就是面向字節流

粘包問題

粘包問題中的 “包” 是應用層的數據包,在 TCP 協議頭部中沒有像 UDP 一樣有一個字段表示報文長度,因此就不知道發送的數據到底有多長,但是 TCP 有一個表示序號的字段

站在傳輸層的角度,數據是編號序號,排好序別放到了接受緩衝區中

站在應用層的角度,數據只是一串連續的字節數據,那麼應用程序看到這麼一串數據,就不知道從哪個位置到哪個位置是一個完整的應用層數據包

避免粘包問題: 明確邊界

  1. 對於定長的數據包,我們只需要保證每次都以相同的長度從接受緩衝區中讀取即可

  2. 對於變長的數據包,我們可以在數據包的頭部,約定一個字段表示數據包的總長度

  3. 對於變長的數據包,我們還可以在包與包之間用分割符分割開,分隔符協議由應用層自己實現

TCP 異常情況

進程終止: 進程終止會釋放文件描述符, 仍然可以發送FIN. 和正常關閉沒有什麼區別.

機器重啓: 和進程終止的情況相同

機器掉電 / 網線斷開:

  • 接收端掛了:發送端收不到 ACK 超時重傳,達到一定次數嘗試進行復位 RST(重新建立連接),如果還不行就釋放連接
  • 發送端掛了:接收端也不會無限的等下去,TCP 內置了保活定時器機制(心跳包),定時向對端發送一個心跳包詢問對端是否還在,不在了釋放連接

小結
在這裏插入圖片描述

TCP/UDP 對比

  1. 如果需要可靠性(比如外網複雜的網絡環境),優先考慮 TCP

  2. 如果網絡結構本身比較簡單,可靠性比較高(機房內網),對傳輸效率要求更高,優先考慮 UDP

  3. 如果你要傳輸的數據報比較大,選TCP(UDP的包有最大長度64k)

  4. 如果需要廣播的話,只能用 UDP,TCP 不能用於廣播

用UDP實現可靠傳輸

  • 引入序列號,確保數據有序
  • 因此 ACK 應答機制,確保數據發送成功
  • 引入超時重傳機制,一段時間未收到 ACK 應答,就重發數據
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章