1. 程式人生 > 其它 >《計算機網路傳輸層 TCP協議》

《計算機網路傳輸層 TCP協議》

《計算機網路傳輸層 TCP協議》

1. TCP 協議特點

  • 在 IP 協議之上 ,解決網路通訊可依賴問題

    • 點對點(不能廣播,多播),面向連線
    • 雙向傳遞 (全雙工)
    • 位元組流傳輸:打包成報文段、保證有序接收、重複報文自動丟棄
      • 缺點: 不維護應用報文的邊界(對比HTTP、GRPC)
      • 優點: 不強制要求應用必須離散的建立資料塊、不限制資料塊的大小
    • 流量緩衝: 解決速度不匹配問題
    • 可靠的傳輸服務 (保證可達、丟包時通過重發時延實現可靠性)
    • 擁塞控制
  • 既然TCP是面向連線的,那麼如何標識一個連線?

    • 通過TCP的四元組(源地址,源埠,目的地址,目的埠)

      • 所以對於 IPV4 地址,單主機最大TCP連線數為\[2^{32+16+32+16} \]

2. TCP協議的任務

  • 主機內的程序定址(通過埠號)
  • 建立、管理、終止連線
  • 處理並將位元組 (8 bit) 流 打包成報文段 ( 如IP報文 )
  • 傳輸資料
  • 保持可靠性與傳輸質量
  • 流量控制與擁塞避免

3. TCP Segment 報文段

每個TCP報文段由固定的20Byte頭部組成,TCP報文頭部 選項可以跟在固定標頭之後。 帶有標頭,使其最多可以標記 65535 個數據位元組。

  • 16位源埠號和16位目的埠號。

  • 32位序號:一次TCP通訊過程中某一個傳輸方向上的位元組流的每個位元組的編號,通過這個來確認傳送的順序

  • 32位確認號:用來響應TCP報文段,給收到的TCP報文段的序號加1,三握時還要攜帶自己的序號。

  • 4位頭部長度:標識該TCP頭部有多少個4位元組,共表示最長15*4=60位元組。同IP頭部。

  • 6位保留。6位標誌。URG(緊急指標是否有效)ACK(表示確認號是否有效)PSH(提示接收端應用程式應該立即從TCP接收緩衝區讀走資料)RST(表示要求對方重新建立連線)SYN(表示請求建立一個連線)FIN(表示通知對方本端要關閉連線)

  • 16位視窗大小:TCP流量控制的一個手段,用來告訴對端TCP緩衝區還能容納多少位元組。

  • 16位校驗和:由傳送端填充,接收端對報文段執行CRC演算法以檢驗TCP報文段在傳輸中是否損壞。

  • 16位緊急指標:一個正的偏移量,它和序號段的值相加表示最後一個緊急資料的下一位元組的序號。

4. TCP 三次握手及狀態變遷

4.1 為什麼TCP不能兩次握手進行連線?

​ TCP 連線需要完成兩項工作一是做好傳送資料前的準備工作(即雙方都知道對方準備好了) 二是完成序列號(sequence number )的同步,這個序列號在握手的過程中被髮送和確認

​ 根據上述如果進行兩次連線 Server 在接收到Client的SYN請求時直接進入到Establishment狀態,並且給Client傳送 SYN和ACK 包。但是這個SYN+ACK 包在傳輸的過程中可能丟失,因此Client無法準確的知道Server是否建立起連線。因此Client會忽略Server傳送過來資料包,只等連線確認包。而Server在傳送資料包分組超時後會重複傳送超時的資料包,這樣就造成了死鎖。

​ 上述回答是通過一個部落格看到了,但是我從RFC793 [page 32] 文件看到了如下的說明:

 The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.  To deal with this, a special control message, reset, has been devised.  If the receiving TCP is in a  non-synchronized state (i.e., SYN-SENT,SYN-RECEIVED), it returns to LISTEN on receiving an acceptable reset. If the TCP is in one of the synchronized states (ESTABLISHED,FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT), it aborts the connection and informs its user.  We discuss this latter case under "half-open" connections below.

大概意思就是說三次握手是為了防止舊的重複初始化連線造成混亂

client發出的第一個連線請求報文段並沒有丟失,而是在某個網路結點長時間的滯留了,以致延誤到連線釋放以後的某個時間才到達server。本來這是一個早已失效的報文段。但server收到此失效的連線請求報文段後,就誤認為是client再次發出的一個新的連線請求。於是就向client發出確認報文段,同意建立連線。假設不採用“三次握手”,那麼只要server發出確認,新的連線就建立了。由於現在client並沒有發出建立連線的請求,因此不會理睬server的確認,也不會向server傳送資料。但server卻以為新的運輸連線已經建立,並一直等待client發來資料。這樣,server的很多資源就白白浪費掉了。採用“三次握手”的辦法可以防止上述現象發生。例如剛才那種情況,client不會向server的確認發出確認。server由於收不到確認,就知道client並沒有要求建立連線。”

4.2 TCP 在Handshake中如果第三次傳送包發生了丟失會怎麼樣?

  • 伺服器角度: 如果第三次ACK確認包丟失。Server的狀態為SYN_REVD ,並根據TCP超時重傳機制,會等待 3秒、6秒、12秒 後重新發送SYN+ACK包,以便Client重新發送ACK包。 而Server重發SYN+ACK包的次數,可以通過設定/proc/sys/net/ipv4/tcp_synack_retries修改,預設值為5.

    如果重發指定次數之後,仍然未收到 client 的ACK應答,那麼一段時間後,Server自動關閉這個連線。

  • 客戶端角度:因為客戶端傳送完ACK包已經進入了Establishment狀態。客戶端向服務端傳送資料,服務端會以RST包進行相應,客戶端就能感知Server的連結錯誤了。

5. TCP的可靠傳輸

5.1 TCP的流與報文段

  • 流分段的依據

    • MSS: 防止IP層分段 IP層分段的效率低下

    • 流控制: 控制端的接收能力

  • MSS(Max Segment Size):僅指TCP承載資料,不包含TCP的頭部長度

  • MSS選擇的目的

    • 儘量每個Segment攜帶更多的資料 以減少TCP頭部空間佔用率
    • 防止Segment被某個裝置的IP層基於MTU拆分
  • 預設 MSS 536位元組 (預設MTU 576個位元組,20位元組IP頭部,20位元組TCP頭部)

5.2 TCP是如何實現可靠傳輸的?

  • TCP通過序號機制和重傳機制來實現TCP位元組的可靠傳輸的

  • Sequence 序列和 ACK確認號設計目的

    • 跟蹤應用層的傳送端資料是否達到

    • 確定接收端有序的接收到字元流

  • 序列號的值針對的是位元組而不是報文

5.2.1 序列號存在的問題

在TCP報文頭部序列號(Sequence Number)所佔32bit 也就是最多能表達的位元組也就是2^32 個也就是4GB,當如果一次TCP連線傳輸的資料超過4G時,在1-4G傳輸過程中,如果資料報傳輸有延遲晚到底,可能會讓接收端分不清那個是先用這個序列號的,那個報文是後用這個序號的

這個也就是PAWS問題(Protect Against Wrapped Sequence numbers )防止序列號迴繞

5.3 RTT時間與RTO時間

下圖是RTT的圖示,簡單的來說就是一個報文從傳送到接收到響應的時間,但是下面是理想的情況,現實中的計算比這複雜多了

RTO是超時重傳時間,RTO設定應該比RTT略大,如果設定太小,可能資料報已經被ACK了,但是由於超時重發時間設定的太短,就又重發了,重發了本應該不用重發的資料報。如果設定的太大就會導致傳輸的效率太低,如果ACK資料報丟失,需要很長時間來進行重發。

5.4 滑動視窗

6. TCP 擁塞控制

6.1 慢啟動

cwnd: 擁塞視窗大小

rwnd: 接收方視窗大小

慢啟動的思想就是為傳送方增加了一個擁塞視窗記為cwnd,擁塞視窗指的是接收到ACK後,傳送端還能傳送最大的MSS數,傳送方的視窗大小 = Min {cwnd,rwnd}。

慢啟動演算法就是,每收到一個ACK (eg.也就是經過一個RTT) cwnd的大小*2 ,比如最開始cwnd的大小是 initcwnd,當接收到該報文的ACK ,cwnd的大小就變為initcwnd * 2 。下次傳送報文段的數量就是 initcwnd *2 ,再接收到 initcwnd * 2 報文段的ACK後,那麼接著傳送視窗的大小就是 initcwnd * 4 ,這是一種指數關係

\[CWND = initcwnd * 2^n \]

n 指的是RTT的次數

6.2 擁塞避免

慢啟動閾值:ssthresh

當cwnd<ssthresh時,擁塞視窗使用慢啟動演算法,按指數級增長。 當cwnd="">ssthresh時,擁塞視窗使用擁塞避免演算法,按線性增長。

擁塞避免演算法每經過一個RTT,擁塞視窗增加initcwnd

當到達設定的閾值時傳送視窗的大小就每收到一個ACK,cwnd+=initcwnd ,當出現丟包的情況是就立馬將傳送視窗的大小設定為 initcwnd.

當發生擁塞的時候(超時或者收到重複ack),RFC5681認為此時ssthresh需要置為沒有被確認包的一半,但是不小於兩個MSS。此外,如果是超時引起的擁塞,則cwnd被置為initcwnd

超時重傳對傳輸效能有嚴重影響。原因之一是在RTO階段不能傳資料,相當於浪費了一段時間;原因之二是擁塞視窗的急劇減小,相當於接下來傳得慢多了。

6.3 快速重傳

有時候擁塞比較輕微,只有少量包丟失,後續的包能夠正常到達。當後續的包到達接收方時,接收方會發現其Seq號比期望的大,所以它每收到一個包就Ack一次期望的Seq號,以此提醒傳送方重傳。當傳送方收到3個或以上重複確認(Dup Ack)時,就意識到相應的包已經丟了,從而立即重傳它。這個過程稱為快速重傳。

為什麼要規定湊滿3個呢?這是因為網路包有時會亂序,亂序的包一樣會觸發重複的Ack,但是為了亂序而重傳沒有必要。由於一般亂序的距離不會相差太大,比如2號包也許會跑到4號包後面,但不太可能跑到6號包後面,所以限定成3個或以上可以在很大程度上避免因亂序而觸發快速重傳。

還有一個問題,如下圖:

如果2號和3號包都丟失了,但是後面4,5,6,7號都正常收到了,並觸發了三次ack = 2。在重傳了2號包之後該傳哪個包那,是全部需要重傳還是隻傳2號包?

為了解決這種問題,TCP在傳送重複的Ack包的時候,會告訴接收方收到的已經收到包的序號,如下圖:

這樣傳送方就知道該重傳哪個包了,這種方式被稱為選擇性確認(Selective Acknowledgement)

6.4快速恢復

如果在擁塞階段發生了快速重傳就沒有必要像超時重傳那樣處理擁塞視窗了,因為此時的擁塞並不是很嚴重。RFC5681建議此時的慢啟動閾值ssthreh設定為沒有被確認包的1/2,但是不小於2個MSS。擁塞視窗設定為慢啟動閾值加3個MSS。這個過程被稱為快速恢復

7.1 TCP的四次揮手

TCP的四次揮手如下圖所示

7.1 為什麼TCP建立連線需要三次握手,而釋放連線需要四次?

當處於Listen狀態的伺服器接收到一個SYN報文後,可以將ACK(應答作用)報文和SYN (請求建立連線) 報文放到一個報文中進行傳送。

但是在關閉TCP連線時,對方給你傳送一個FIN報文,這僅僅表示對方沒有資料給你傳送了,但是並不代表你沒有資料給對方傳送了。所以先給對方傳送一個ACK報文,當傳送完資料過後在傳送一個FIN報文表示告訴對方你同意關閉連線了。所以在通常情況下ACK報文和FIN報文是分開發送的。

7.2 為什麼TIME_WAIT 要等待2 * MSL秒後才釋放連線?

  1. 可靠的實現全雙工通訊的終止

    TCP協議在關閉連線的4次握手中,最終ACK必須由發起終止的主機(A端)傳送,如果這個ACK丟失,那麼被終止端會重新發送FIN包。因此A端必須維護TIME_WAIT 允許他最終傳送FIN包。如果A端不維護TIME_WAIT 而是直接處於Closed狀態,那麼A端會給B相應一個RST,B端在收到會解析成一個錯誤(在Java中會丟擲異常)

  2. 允許老的重複位元組在網路中消失

    TCP分節可能由於路由器異常而“迷途”,在迷途期間,TCP傳送端可能因確認超時而重發這個分節,迷途的分節在路由器修復後也會被送到最終目的地,這個遲到的迷途分節到達時可能會引起問題。在關閉“前一個連線”之後,馬上又重新建立起一個相同的IP和埠之間的“新連線”,“前一個連線”的迷途重複分組在“前一個連線”終止後到達,而被“新連線”收到了。為了避免這個情況,TCP協議不允許處於TIME_WAIT狀態的連線啟動一個新的可用連線,因為TIME_WAIT狀態持續2MSL,就可以保證當成功建立一個新TCP連線的時候,來自舊連線重複分組已經在網路中消逝。

</ssthresh時,擁塞視窗使用慢啟動演算法,按指數級增長。>