一、TCP協定的特點
TCP全稱為 “傳輸控制協定”(Transmission Control Protocol),顧名思義, 要對資料的傳輸進行一個詳細的控制。有如下特點:
1、面向連接配接的
2、面向位元組流
3、保證可靠傳輸(丢包重發,逾時重傳)
4、支援全雙工通信
5、支援端口到端口的連接配接,每一條TCP連接配接隻能有兩個端點
二、TCP協定段格式
如下圖:
解釋各字段的意思:
1、源/目的端口号: 表示資料是從哪個程序來, 到哪個程序去;
2、32位序号/32位确認号:互相協同,保證可靠性,保證按序到達。
3、4位TCP報頭長度: 表示該TCP頭部有多少個32位bit(有多少個4位元組); 是以TCP頭部最大長度是15 * 4 = 60
4、6位标志位:
URG: 緊急指針是否有效
ACK: 确認号是否有效
PSH: 提示接收端應用程式立刻從TCP緩沖區把資料讀走
RST: 對方要求重建立立連接配接,我們把攜帶RST辨別的稱為複位封包段
SYN: 請求建立連接配接,我們把攜帶SYN辨別的稱為同步封包段
FIN: 通知對方, 本端要關閉了, 我們稱攜帶FIN辨別的為結束封包段
5、16位視窗大小:自己的接收緩沖區大小(下文流量控制有講解)
6、6位校驗和: 發送端填充, CRC校驗. 接收端校驗不通過, 則認為資料有問題. 此處的檢驗和不光包含TCP首部, 也包含TCP資料部分.
7、16位緊急指針: 辨別哪部分資料是緊急資料。
三、TCP的連接配接建立
1、三次握手
過程如下:
解析:
(1)服務端狀态轉化:
[CLOSED->LISTEN]:伺服器端調用listen後進入LISTEN狀态, 等待用戶端連接配接。[LISTEN->SYN_RCVD]:一旦監聽到連接配接請求(同步封包段),就将該連接配接放入核心等待隊列中, 并向用戶端發送SYN确認封包。
[SYN_RCVD -> ESTABLISHED]:服務端一旦收到用戶端的确認封包, 就進入ESTABLISHED狀态, 可以進行讀寫資料了。
(2)用戶端狀态轉化:
[CLOSED -> SYN_SENT]:用戶端調用connect, 發送同步封包段;
[SYN_SENT -> ESTABLISHED]:connect調用成功, 則進入ESTABLISHED狀态, 開始讀寫資料。
2、四次揮手
注意,斷開連接配接是雙方的問題,必須經過雙方同意。
過程如下:
解析:
(1)服務端狀态轉化:
[ESTABLISHED -> CLOSE_WAIT]:當用戶端主動關閉連接配接(調用close), 伺服器會收到結束封包段,伺服器傳回确認封包段并進入CLOSE_WAIT;
[CLOSE_WAIT -> LAST_ACK]:進入CLOSE_WAIT後說明伺服器準備關閉連接配接(需要處理完之前的資料); 當伺服器真正調用close關閉連接配接時, 會向用戶端發送FIN, 此時伺服器進入LAST_ACK狀态, 等待最後一個ACK到來(這個ACK是用戶端确認收到了FIN) [LAST_ACK -> CLOSED]:伺服器收到了對FIN的ACK, 徹底關閉連接配接。
(2)用戶端狀态轉化:
[ESTABLISHED -> FIN_WAIT_1]: 用戶端主動調用close時, 向伺服器發送結束封包段, 同時進入 FIN_WAIT_1;
[FIN_WAIT_1 -> FIN_WAIT_2]:用戶端收到伺服器對結束封包段的确認, 則進入FIN_WAIT_2, 開始等待伺服器的結束封包段;
[FIN_WAIT_2 -> TIME_WAIT]:用戶端收到伺服器發來的結束封包段, 進入TIME_WAIT, 并發出 LAST_ACK;
[TIME_WAIT -> CLOSED]:用戶端要等待一個2MSL的時間, 才會進入CLOSED狀态
注:TCP協定規定,主動關閉連接配接的一方要處于TIME_WAIT狀态,等待兩個MSL(maximum segment lifetime)的時間後才能回到CLOSED狀态,原因在下文。
3、釋放連接配接2MSL問題
MSL,指的是封包最大生存時間,也就是指封包從A端到B端單向的最大傳輸時間。
那等待2MSL的原因是什麼呢?
(1)保證在兩個傳輸方向上的尚未被接收或遲到的封包段都已經消失。
客戶機發送完ACK封包段,經過2MSL時間後,本次連接配接持續時間内所産生的所有的封包段都從網絡當中消失。這樣就可以使得下一個新的連接配接中不會出現這種舊的連接配接請求封包段。
(2)保證四次揮手的時候客戶機發送的最後一個ACK封包段能夠到達伺服器。
假設丢失了,此時伺服器會再重發一個 FIN。這時雖然用戶端的程序不在了, 但是TCP連接配接還在, 仍然可以重發LAST_ACK);
然後客戶機重傳一次ACK确認封包段,重新啟動時間2MSL等待定時器,這樣才能確定客戶機和伺服器都進入CLOSED狀态,防止因為客戶機的ACK封包段的丢失導緻伺服器無法CLOSED。
4、常見問題
(1)為什麼要采用三次握手,二次握手可以嗎?
采用三次握手是為了防止已連接配接的請求封包段又傳送到伺服器,造成伺服器崩潰。
假設隻有兩次握手,連接配接時,第二次丢失,伺服器認為連接配接成功,而用戶端認為連接配接失敗,繼續發送連接配接請求,伺服器就會收到SYN的洪水攻擊。
(2)為什麼建立連接配接協定是三次握手,而關閉連接配接卻是四次握手呢?
因為TCP是全雙工模式,是以每個方向都需要一個FIN和ACK,當一端發送了FIN包之後,處于半關閉狀态,此時仍然可以接收資料包。
在建立連接配接時,伺服器可以把SYN和ACK放在一個包中發送。
假設在斷開連接配接時,如果一端收到FIN包,但此時仍有資料未發送完,此時就需要先向對端回複FIN包的ACK。等到将剩下的資料都發送完之後,才能向對端發送FIN,斷開這個方向的連接配接。
是以很多時候FIN和ACK需要在兩個資料包中發送,則需要四次握手。
四、TCP協定的可靠性
1、校驗和
2、序列号(按序到達)
3、确認應答(ACK)機制
TCP将每個位元組的資料都進行了編号,即為序列号,每一個ACK都帶有對應的确認序列号, 意思是告訴發送者, 我已經收到了哪些資料,下一次你從哪裡開始發。
舉例如下:
4、逾時重傳機制
傳輸資料時,可能會出現如下問題:
(1)主機A發送資料給B之後, 可能因為網絡擁堵等原因, 資料無法到達主機B;
(2)主機A在一個特定時間間隔内沒有收到B發來的确認應答, 會進行重發。
(主機A未收到B發來的确認應答, 也可能是因為ACK丢失了)
解決方法:
這樣的話,主機B就會收到很多重複資料,TCP協定需要能夠識别出那些包是重複的包, 并且把重複的丢棄掉,可以利用前面提到的序列号, 很容易做到去重的效果。
那麼, 逾時的時間長短如何确定?
可能有人會說, 找到一個最小的時間, 保證 “确認應答一定能在這個時間内傳回”。
但是傳輸的時間,随着網絡環境的不同, 是有差異的。
TCP為了保證無論在任何環境下都能比較高性能的通信, 會動态計算這個最大逾時時間.。
(1)Linux中(BSD Unix和Windows也是如此), 逾時以500ms為一個機關進行控制, 每次判定逾時重發的逾時時間都是500ms的整數倍.
(2)如果重發一次之後, 仍然得不到應答, 等待 2*500ms 後再進行重傳.
(3)如果仍然得不到應答, 等待 4*500ms 進行重傳. 依次類推, 以指數形式遞增.
(4)累計到一定的重傳次數, TCP認為網絡或者對端主機出現異常, 強制關閉連接配接。
5、流量控制
可能會遇到如下問題:
如果發送端發的太快,導緻接收端的緩沖區被打滿,這個時候如果發送端繼續發送, 就會造成丢包, 繼而引起丢包重傳等一系列連鎖反應。
我們就需要流量控制機制,
(1)當接受方來不及處理發送方的資料,能提示發送方降低發送的速率,防止包丢失
(2)通過視窗來控制 (視窗大小字段越大, 說明網絡的吞吐量越高)
是以,簡單來說,就是接收方處理不過來資料的時候,就把視窗縮小,并把視窗值告訴發送端。
那麼接收端如何把視窗大小告訴發送端呢?
在第一點講解TCP首部中, 有一個16位視窗字段, 就是存放了視窗的大小資訊。
6、擁塞控制
如果在剛連接配接好的階段就發送大量的資料, 可能引發問題。
因為網絡上有很多的計算機, 可能目前的網絡狀态就已經比較擁堵。在不清楚目前網絡狀态下, 貿然發送大量的資料, 是很有可能引起網絡擁塞的。
慢啟動原理:
1)當主機開始發送資料時,如果立即将較大的發送視窗的全部資料位元組都注入到網絡中,那麼由于不清楚網絡的情況,有可能引其網絡擁塞
2)比較好的方法是試探一下,即從小到大逐漸增大發送端的擁塞控制視窗數值
3)通常在剛剛開始發送封包段時可先将擁塞視窗cwnd設定為一個最大封包段的MSS的數值。
(4)在每收到一個對新封包段确認後,将擁塞視窗增加至多一個MSS的數值,當rwind足夠大的時候,為了防止擁塞視窗cwind的增長引起網絡擁塞,還需要另外一個變量–慢開始門限ssthresh
擁塞控制具體過程為:
1)TCP連接配接初始化,将擁塞視窗設定為1
2)執行慢開始算法,cwind按指數規律增長,直到cwind == ssthress開始執行擁塞避免算法,cwnd按線性規律增長
3)當網絡發生擁塞,把ssthresh值更新為擁塞前ssthresh值的一半,cwnd重新設定為1,按照步驟(2)執行。
五、TCP協定的性能
提高性能:
1、滑動視窗
通過上文我們知道,對每一個發送的資料段,都要給一個ACK确認應答。收到ACK後再發送下一個資料段。
如下:
這樣做有一個比較大的缺點, 就是性能較差。
那我們可以一次發送多條資料, 就可以大大提高性能(其實是将多個段的等待時間重疊在一起了)。
如下:
視窗大小指的是無需等待确認應答而可以繼續發送資料的最大值。
如上圖,上圖的視窗大小就是4000個位元組 (四個段).
(1)發送前四個段的時候, 不需要等待任何ACK, 直接發送;
(2) 收到第一個ACK後, 滑動視窗向後移動, 繼續發送第五個段的資料;
(3) 依次類推;
(4)作業系統核心為了維護這個滑動視窗, 需要開辟發送緩沖區來記錄目前還有哪些資料沒有應答; 隻有确認應答過的資料, 才能從緩沖區删掉;
視窗越大, 則網絡的吞吐率就越高。
那麼?丢包了如何進行重傳?分兩種情況。
(1)資料包已經抵達, ACK被丢了.
這種情況下, 部分ACK丢了并不要緊, 因為可以通過後續的ACK進行确認。
當後續的ACK被收到證明前面的資料包已經抵達。
(2)資料包丢了.
由圖可知,當某一段封包段丢失之後, 發送端會一直收到 1001 這樣的ACK, 就像是在提醒發送端 “我想要的是 1001” 一樣;
如果發送端主機連續三次收到了同樣一個 “1001” 這樣的應答, 就會将對應的資料 1001 - 2000 重新 發送; 這個時候接收端收到了 1001 之後, 再次傳回的ACK就是7001了(因為2001 - 7000)接收端其實之前 就已經收到了, 被放到了接收端作業系統核心的接收緩沖區中。
* 這種機制被稱為 “高速重發控制”(也叫 “快重傳”).*
2、快速重傳
3、延遲應答
我們都知道,視窗越大, 網絡吞吐量就越大, 傳輸效率就越高.
是以我們應該在保證網絡不擁塞的情況下盡量提高傳輸效率。
舉例如下:
假設接收端緩沖區為1M,一次收到了500K的資料; 如果立刻應答, 傳回的視窗就是500K;
但實際上可能處理端處理的速度很快, 10ms之内就把500K資料從緩沖區消費掉了; 在這種情況下, 接收端處理還遠沒有達到自己的極限, 即使視窗再放大一些, 也能處理過來;
如果接收端稍微等一會再應答, 比如等待200ms再應答, 那麼這個時候傳回的視窗大小就是1M;
延遲應答也有限制:
(1)數量限制: 每隔N個包就應答一次;
(2)時間限制: 超過最大延遲時間就應答一次;