1. 程式人生 > 其它 >計算機網路總結(二)——TCP相關知識

計算機網路總結(二)——TCP相關知識

2.TCP相關知識

2.1 TCP短連線和長連線

1.短連線:

Client 向 Server 傳送訊息,Server 迴應 Client,然後一次讀寫就完成了,這時候雙方任何一個都可以發起 close 操作,不過一般都是 Client 先發起 close 操作。短連線一般只會在 Client/Server 間傳遞一次讀寫操作。

短連線的優點:管理起來比較簡單,建立存在的連線都是有用的連線,不需要額外的控制手段。

2.長連線:

Client 與 Server 完成一次讀寫之後,它們之間的連線並不會主動關閉,後續的讀寫操作會繼續使用這個連線。

在長連線的應用場景下,Client 端一般不會主動關閉它們之間的連線,Client 與 Server 之間的連線如果一直不關閉的話,隨著客戶端連線越來越多,Server 壓力也越來越大,這時候 Server 端需要採取一些策略,如關閉一些長時間沒有讀寫事件發生的連線,這樣可以避免一些惡意連線導致 Server 端服務受損;如果條件再允許可以以客戶端為顆粒度,限制每個客戶端的最大長連線數,從而避免某個客戶端連累後端的服務。

長連線和短連線的產生在於 Client 和 Server 採取的關閉策略,具體的應用場景採用具體的策略。

2.2 TCP重傳、滑動視窗、流量控制、擁塞控制

1. 重傳機制

TCP實現可靠傳輸的方式之一,是通過序列號與確認應答。但是在錯綜複雜的網路環境中,很有可能出現數據包丟失的情況,如果資料包丟失了,TCP會用重傳機制解決。

(1)超時重傳

TCP在傳送資料時,會設定一個定時器,如果一個已經發送的報文段在指定時間內沒有收到確認應答報文,那麼就重傳這個報文段。一個報文段從傳送再到接收到確認所經過的時間稱為往返時間 RTT,而超時時間稱為 RTO,RTO的值應略大於RTT,如圖所示:

(2)快速重傳

TCP還有一種快速重傳機制,它不以時間為驅動,而是以資料驅動重傳。

如果傳送端連著收到了三個ACK標誌位相同的確認,則傳送端知道了對應的資料包接收端未收到,就會在定時器過期之前,重傳丟失的資料包。

(3)SACK

如果不知道該重傳哪些TCP報文,可以用SACK方法,這種方式需要在TCP頭部【選項】欄位裡增加“SACK”,將緩衝區的資料傳送給對方,這樣傳送方就知道哪些資料收到了,哪些沒收到。這時,就可以只重傳丟失的資料。

2. 滑動視窗

TCP因為每傳送一個數據,都要進行一次確認應答,當上一個數據包收到應答了,再發送下一個。但是這樣效率非常低,所以TCP引入視窗的概念,視窗是快取的一部分,用來暫時存放位元組流。

視窗可以設定大小,視窗大小就是無需等待確認應答,而可以繼續傳送資料的最大值。傳送方和接收方各有一個視窗,接收方通過 TCP 報文段中的視窗欄位告訴傳送方自己的視窗大小,傳送方根據這個值和其它資訊設定自己的視窗大小。傳送方傳送的資料大小不能超過接收方的視窗大小。假設視窗大小為3個TCP段,那麼傳送方就可以連續傳送3個TCP段,並且如果中途有ACK丟失,可以通過下一個確認應答進行確認。如圖所示:

傳送方和接收方的視窗示例如下圖所示,其中傳送方視窗分為4個部分,接收方視窗分為3個部分:

如果傳送視窗左部的位元組已經發送並且收到了確認,那麼就將傳送方視窗向右滑動一定距離,直到左部第一個位元組不是已傳送並且已確認的狀態,接收視窗的滑動類似,接收視窗左部位元組已經發送確認並交付主機,就向右滑動接收視窗。

上圖中,假設傳送視窗中的31~33位元組的ACK確認應答後,如果傳送視窗大小未發生變化,則傳送視窗向右滑動3個位元組,與之對應的可用視窗也向右滑動3個位元組,那麼後續可以傳送41~43這3個位元組的資料。類似的,接收視窗收到資料後,也向右滑動3個位元組。

3. 流量控制

如果接收方處理不過來資料,而傳送方又一直髮送資料的話,就會觸發重傳機制,從而導致網路流量的浪費。為了解決這種問題,TCP可以進行流量控制,讓傳送方根據接收方的實際接收能力控制傳送的資料量,避免傳送方的資料填滿接收方的快取。傳送端主機會時不時的傳送一個叫做視窗探測的資料段,此資料段僅包含一個位元組來獲取最新的視窗大小資訊。即TCP通過讓接收方指明希望從傳送方接收的資料大小(視窗大小)來進行流量控制

傳送視窗和接收視窗中所存放的欄位,都是放在作業系統記憶體緩衝區的,而作業系統的緩衝區,會被作業系統調整。如果服務非常繁忙,作業系統可能會減少緩衝區的大小,如果此時應用程式沒有讀取資料,將收到的資料留在緩衝區中,會收縮視窗,就會出現丟包的現象。例如:傳送資料大小超過了接收窗的大小,就會丟包。所以TCP規定不允許同時減少緩衝區又收縮視窗,只能先收縮視窗,過段時間再減少快取

如果接收視窗大小為0,就會阻止傳送方給接收方傳遞資料,知道視窗變為0位置,這就是視窗關閉。

4. 擁塞控制

計算機網路時常會出現擁堵現象,這時如果傳送方傳送大量資料包,就可能會導致資料包時延,丟失等。這時TCP就會重傳資料,從而加重網路負擔,形成惡性迴圈。所以當網路發生擁塞時,TCP會進行擁塞控制。

擁塞控制是傳送方維護的一個的狀態變數,它會根據網路的擁塞程度動態變化。這一點和流量控制很像,但是出發點不同。流量控制是為了讓接收方能來得及接收,而擁塞控制是為了降低整個網路的擁塞程度

引入擁塞控制後,傳送視窗的值是 swnd = min(cwnd, rwnd),也就是擁塞視窗和接受視窗的最小值。只要網路中沒出現擁塞,cwnd 就會增大,但網路中出現了擁塞,cwnd就會減少。一般來說,如果傳送方傳送超時重傳,就會認為網路中出現了擁塞

TCP 主要通過四個演算法來進行擁塞控制:

慢開始、擁塞避免、快速重傳、快速恢復。

(1)慢開始與擁塞避免

TCP在剛建立連線完成後,首先是有個慢開始的過程,一點一點的提高發送資料包的數量。當傳送方每收到一個ACK,擁塞視窗cwnd的大小就會加1。例如,開始時,令 cwnd = 1,傳送方只能傳送 1 個報文段;當收到確認後,將 cwnd 加倍,因此之後傳送方能夠傳送的報文段數量為:2、4、8 ... 。

這裡設定一個慢開始門限 ssthresh,當 cwnd >= ssthresh 時,進入擁塞避免,當傳送方每收到一個ACK,擁塞視窗cwnd的大小就會加1/cwnd,即每個輪次只將 cwnd 加 1。如果出現了超時,則令 ssthresh = cwnd / 2,然後重新執行慢開始。

(2)快速重傳和快速恢復

擁塞發生時,會用到前面討論過的重傳機制中的方法,即超時重傳和快速重傳。在接收方,要求每次接收到報文段都應該對最後一個已收到的有序報文段進行確認。例如已經接收到 M1 和 M2,此時收到 M4,應當傳送對 M2 的確認。在傳送方,如果收到三個重複確認,那麼可以知道下一個報文段丟失,此時可能會發生超時重傳,重新進入慢開始,但是這樣會突然減少資料流,反應強烈容易造成網路卡頓,更多的採用快速重傳的方式,立即重傳下一個報文段。例如收到三個 M2,則 M3 丟失,立即重傳 M3。

在快速重傳後,只是丟失個別報文段,而不是網路擁塞。因此執行快速恢復,令 ssthresh = cwnd / 2 (也就是設定為原來的一半),cwnd = ssthresh,重傳丟失的資料包。如果收到新的ACK,說明ACK確認了新的資料,恢復過程已經結束,可以進入到之前的狀態,也就是擁塞避免狀態。如圖所示:

注意慢開始和快速恢復的快慢指的是 cwnd 的設定值。擁塞控制的整個過程如圖所示(圖中第2步為超時重傳,第3步為快速重傳):

2.3 TCP粘包、拆包問題及解決辦法

(1)TCP粘包、拆包的概念

TCP有粘包和拆包問題,而UDP沒有,UDP 是基於報文傳送的,UDP首部採用了 16bit 來指示 UDP 資料報文的長度,因此在應用層能很好的將不同的資料報文區分開,從而避免粘包和拆包的問題。

TCP 是基於位元組流的,雖然應用層和 TCP 傳輸層之間的資料互動是大小不等的資料塊,但是 TCP 並沒有把這些資料塊區分邊界,僅僅是一連串沒有結構的位元組流;另外從 TCP 的幀結構也可以看出,在 TCP 的首部沒有表示資料長度的欄位,基於上面兩點,在使用 TCP 傳輸資料時,才有粘包或者拆包現象發生的可能。

假設 Client 向 Server 連續傳送了兩個資料包,用 packet1 和 packet2 來表示,那麼服務端收到的資料可以分為三種情況,現列舉如下:

1)第一種情況,接收端正常收到兩個資料包,即沒有發生拆包和粘包的現象。

2)第二種情況,接收端只收到一個數據包,但是這一個資料包中包含了傳送端傳送的兩個資料包的資訊,這種現象即為粘包。這種情況由於接收端不知道這兩個資料包的界限,所以對於接收端來說很難處理。

3)第三種情況,接收端收到了兩個資料包,但是這兩個資料包要麼是不完整的,要麼就是多出來一塊,這種情況即發生了拆包和粘包。這兩種情況如果不加特殊處理,對於接收端同樣是不好處理的。

(2)TCP 粘包、拆包發生原因

  • 要傳送的資料大於 TCP 傳送緩衝區剩餘空間大小,將會發生拆包。

  • 待發送資料大於 MSS(最大報文長度),TCP 在傳輸前將進行拆包。

  • 要傳送的資料小於 TCP 傳送緩衝區的大小,TCP 將多次寫入緩衝區的資料一次傳送出去,將會發生粘包。

  • 接收資料端的應用層沒有及時讀取接收緩衝區中的資料,將發生粘包。

(3)TCP粘包、拆包解決辦法

由於 TCP 本身是面向位元組流的,無法理解上層的業務資料,所以在底層是無法保證資料包不被拆分和重組的,這個問題只能通過上層的應用協議棧設計來解決,根據業界的主流協議的解決方案,歸納如下:

  • 訊息定長:傳送端將每個資料包封裝為固定長度(不夠的可以通過補 0 填充),這樣接收端每次接收緩衝區中讀取固定長度的資料就自然而然的把每個資料包拆分開來。

  • 設定訊息邊界:服務端從網路流中按訊息邊界分離出訊息內容。在包尾增加回車換行符進行分割,例如 FTP 協議。

  • 將訊息分為訊息頭和訊息體:訊息頭中包含表示訊息總長度(或者訊息體長度)的欄位。

  • 更復雜的應用層協議比如 Netty 中實現的一些協議都對粘包、拆包做了很好的處理。

參考:

  1. 《TCP-IP網路程式設計》 韓-尹聖雨
  2. 《圖解網路》小林coding

  3. 《圖解TCP/IP》

  4. https://zhuanlan.zhihu.com/p/108822858