TCP三次握手、四次揮手、滑動窗口、流量控制
- 2020 年 1 月 19 日
- 筆記
在學習TCP協議之前,我們了解一下TCP頭的報文格式

- 最上層是源埠號和目標埠號
- 接下來是包的序號和確認序號,這裡的序號的作用就是維持TCP的順序性和可靠性兩種特性
- 接下來左邊部分是當前的狀態位,而大名鼎鼎的三次握手和四次揮手就是基於這個狀態位實現的
- 右半部分是滑動窗口,主要是為了實現流量控制和擁塞控制
記住這個報文格式,接下來的內容都是基於這個的
三次握手
首先我們從一個兩個人打電話的例子來描述一下:
- A:哈嘍,B。能聽到么?
- B:你好,A。我能聽到,你能聽到么?
- A:太好了,我也能聽到。我們來嘮嗑么。
這個過程其實可以完美的解釋三次握手的機制。我們知道,網路環境總是不安全的,只有至少經過這三次交互才能確認兩方的發送和接受能力都沒問題。我們來看一下這幾步分別確定了什麼:
- 第一回合:當A將要跟B通訊時,它會先發出去一個包。
- 第二回合:當B收到這個包的時候,就可以確定A的發送能力沒問題,自己的接受能力沒問題,它就會給A一個回復說:我收到了你的包了。
- 第三回合:A收到B的返回資訊後就可以確定自己的發送和接受都沒有問題,B的接受和發送能力都沒有問題。但是這個時候B還不能確定自己的發送能力,所以只有當A再次發送一個包告訴B我收到你的包了才算是完成了這個握手。
三次握手除了上方的連接之外,還會牽扯到我們文章剛開始時說的包的序號和狀態位,下面這個圖就是一個完整的三次握手流程

一開始,A和B都處於 CLOSED
狀態。先是B主動監聽某個埠,處於 LISTEN
狀態。然後A主動發起連接 SYN
,之後處於 SYN-SENT
狀態。B收到發起的連接,返回 SYN
,並且 ACK
A的 SYN
,之後處於 SYN-RCVD
狀態。A收到B發送的 SYN
和 ACK
之後,發送 ACK
的 ACK
,之後處於 ESTABLISHED
狀態,因為它一發一收成功了。服務端收到 ACK
的 ACK
之後,處於 ESTABLISHED
狀態,因為它也一發一收了
四次揮手
理解了三次握手之後再看四次揮手就比較簡單了。直接看圖

當消息傳輸完畢後,斷開鏈接的過程就是這樣的:
- A:我要掛了哈,拜拜
- B:嗯嗯,好的
- B:我也要掛了,拜拜
- A:嗯嗯,好的。等待2MSL如果沒有異常場景就結束了
上方有一個欄位MSL,它是報文在網路上的最大生存時間。那麼為什麼要等待2MSL才會斷開鏈接呢?
主要是因為報文的最大生存時間是1MSL,如果A直接關閉的話,它佔用的埠如果馬上建立了一個新的連接,那麼新的連接就有可能收到剛才B發送的遲到的報文,從而誤以為是發給自己的
TCP消息的順序性和可靠性
剛才我們已經知道了,TCP中每一個包都有一個序號,為了保證順序性和可靠性,TCP的發送方和接受方都是需要記錄這些序號相關的數據
為了記錄所有發送的包,發送端需要記錄以下這些場景的數據:
- 發送了並且已經確認的
- 發送了並且尚未確認的
- 沒有發送,但是已經等待發送的
- 沒有發送,並且暫時還不會發送的
也就是如下圖這樣

而接收端記錄的內容要簡單一些:
- 接受並且確認過的
- 還沒接收,但是馬上就能接收的
- 還沒接收,也沒法接收的

基於上面兩個圖,我們來研究一下順序性和可靠性:
在發送端來看,1、2、3 已經發送並確認;4、5、6、7、8、9 都是發送了還沒確認;10、11、12 是還沒發出的;13、14、15 是接收方沒有空間,不準備發的。
在接收端來看,1、2、3、4、5 是已經完成 ACK
,但是沒讀取的;6、7 是等待接收的;8、9 是已經接收,但是沒有 ACK
的。
發送端和接收端當前的狀態如下:
- 1、2、3 沒有問題,雙方達成了一致
- 4、5 接收方說
ACK
了,但是發送方還沒收到,有可能丟了,有可能在路上。 - 6、7、8、9 肯定都發了,但是 8、9 已經到了,但是 6、7 沒到,出現了亂序,快取著但是沒辦法
ACK
。
而當發生丟包問題,或者不能保證有序性時,有這幾種方式可以處理:超時重傳、快速重傳、SACK
TCP滑動窗口
在TCP的傳輸的時候,我們知道如果一個數據比較大的話就會把這個數據劃分為多個包來進行發送。而為了保證消息的順序性和可靠性的話就必須如下圖所示當一個包確認後才能發送下一個包

但是這樣一來就會存在一個問題,每發送一個數據包,都需要得到接收端的確認應答以後才能繼續,那這個等待確認包的過程中就會很浪費時間。
為了應對這種場景,TCP協議就引入了滑動窗口的概念,再次拿來剛才用到的這個圖

這裡我們假設滑動窗口的大小為9不變的,也就是說這個機制可以一次性的發送一個窗口數量的包。這個時候每當收到幾個應答包的時候這個窗口就會後移一個位置,假如這個時候收到了4號消息的ack,那麼這個數據就成這樣了

這樣一個窗口根據消息的ack
不斷後移的過程就稱為滑動窗口
TCP流量控制
上方說到了TCP發送方的滑動窗口,但是接收端處理數據包的能力是不同的,比如說:
- 如果窗口過小,發送端發送少量的數據包,接收端很快就處理了,並且還能處理更多的數據包。這樣,當傳輸比較大的數據時需要不停地等待發送方,造成很大的延遲
- 如果窗口過大,發送端發送大量的數據包,而接收端處理不了這麼多的數據包,這樣,就會堵塞鏈路。如果丟棄這些本應該接收的數據包,又會觸發重發機制
為了避免這種現象的發生,TCP提供了流量控制的機制,我們剛才說到的假設的滑動窗口的大小是固定的,而流量控制就是根據接收端的能力而去調整窗口的大小,這個流程是這樣的:
發送端第一次以窗口大小(該窗口大小是根據鏈路頻寬的大小來決定的)發送數據包,接收端接收這些數據包,並返回確認應答包,告訴發送端自己下次希望收到的數據包是多少(新的窗口大小),發送端收到確認應答包以後,將以該窗口大小進行發送數據包