TCP協議簡單理解

tcp全稱是“傳輸控制協議”。人如其名,要對數據的傳輸進行一個詳細的控制。
tcp核心設計:
1.保證可靠性傳輸 能夠及時的發現自己發送數據是成功還是失敗
2.儘可能的提高傳輸效率
1,2進行權衡

TCP協議段格式

在這裏插入圖片描述從左到右,唐僧問路,16位源端口,16位目的端口,32位序號,32位確認序號,4位首部長度,16位窗口大小,16位檢驗和,16位緊急指針,選項,數據。

4位TCP報頭長度:表示該TCP頭部有多少個32位bit(有多少個4個字節);所有TCP頭部最大長度是15*4=60字節

6位標誌位

  • UGR:緊急指針是否有效
  • ACK:確認號是否有效,確認報文段
  • PSH:提示接收端應用程序立刻從TCP緩衝區把數據讀走
  • RST:對方要求重新建立鏈接,我們把攜帶RST標識的稱爲復位報文段
  • SYN:請求建立連接,我們把攜帶SYN標識的稱爲同步報文段
  • FIN:通知對方,本端要關閉了,我們稱攜帶FIN標識的爲結束報文端

16位校驗和:發送端填充,CRC校驗。接受端校驗不通過,則認爲數據有問題。此處的校驗和不光包含TCP首部,也包含TCP數據部分。
16位緊急指針:標識那部分數據是緊急數據

確認應答機制

TCP將每個字節的數據都進行了編號,即爲序列號。
每個ACK都帶有對應的確認序列號,(32位確認序號)意思是告訴發送者,我已經收到了哪些數據;下一次你從哪裏開始發.

超時重傳機制

  1. 超時時間依次變長(悲觀的態度去面對丟包)
  2. 自動去重(通過序號進行去重),放到接收緩衝區裏,接收包會有緩衝區的數據進行對比(序號),如有有的話,就會丟掉。
  3. 累計到一定的重傳次數,TCP認爲網絡或者對端主機出現異常,強制關閉連接。

連接管理機制

  1. 建立連接(三次握手) syn(同步報文段,那個比特位設置位爲1)與ack(確認報文段,那個比特位爲1)都是內核控制發送的,第二次響應一起發送(同一時刻)。
    如果客戶端第一次syn丟包,超時重傳,如果客戶端第一次ack 失敗,超時重傳。
    檢查網絡的聯通狀態,協商一些關鍵性數據。
    假設,第三次ack發送丟包,服務器沒有收到ack,客戶端不知道ack是否成功時,當客戶端發送傳輸數據時,就會發現服務器沒有進入ESTABLISHED狀態,就會重新進行三次握手,建立連接。

  2. 斷開連接(四次揮手)客戶端發送FIN(結束報文段),服務器發送ack,發送FIN(兩次不一定同時發送)。

服務端狀態變化:

  • [CLOSED->LISTEN] 服務器端調用listen後進入LISTEN狀態,等待客戶端連接
  • [LISTEN->SYN_RCVD]一旦監聽到連接請求(同步報文段),就將該連接放入內核當待隊列中,並向客戶端發送SYN確認報文。
  • [SYN_RCVD->ESTABLISHED]服務器一旦收到客戶端的確認報文,就進入ESTABLEISHED狀態,可以進行讀寫數據了
  • [ESTABLISHED->CLOSE_WAIT]當客戶端主動關閉連接(調用close),服務器會收到結束報文段,服務器返回確認報文段並進入CLOSE_WAIT
  • [CLOSE_WAIT->LAST_ACK]進入CLOS_WAIT後說明服務器準備關閉連接(需要處理完之前的數據);當服務器真正調用close關閉連接時,會向客戶端發送FIN,此時服務器進入LAST_ACK狀態,等待最後一個ACK到來(這個ACK是客戶端確認收到了FIN)
  • [LAST_ACK->CLOSED]服務器收到了對FIN的ACK,徹底關閉了連接

客戶端狀態轉化

  • [CLOSED->SYN_SENT]客戶端調用connect,發送同步報文段;
  • [SYN_SENT->ESTABLISHED]connect調用成功,則進入ESTABLISHED狀態,開始讀數據
  • [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(MAX SEGMENT LIFE,報文最大生存時間)的時間,纔會進入CLOSED狀態

理解TIME_WAIT狀態

如果我們先啓動server,然後啓動client,然後Ctrl-C使server終止,這是馬上再運行server,結果是

$./server
bind error:Address already in use

雖然server的應用程序終止了,但是TCP協議層的連接並沒有完全斷開,因此不能再次監聽同樣的server端,我們用natsat命令查看一下:
發現服務器所處於的狀態。

  • TCP協議規定,主動關閉連接的一方要處於TIME_WAIT狀態,等待兩個MLS(報文最大生存時間)的時間才能回到CLOSED狀態。
  • 我們使用Ctrl+c終止了server,所以server是主動關閉連接的一方,在TIME_WAIT期間仍然不能再次監聽同樣的server端口
  • MSL在RFC1122中規定爲2分鐘,但是各操作系統的實現不同,在Centos7默認配置的值是60s
  • 可以通過cat/proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值

爲什麼TIME_WAIT的時間是2MSL?

  • MSL是TCP報文的最大生存時間,因此TIME_WAIT持續存在2MSL的話,就能保證在兩個傳輸方式上的尚未被接收或遲到的報文段都已經消失(否則服務器立刻重啓,可能會收到來自上一個進程的遲到的數據,但是這種數據很可能是錯誤的);同時也是理論上保證最好一個報文可靠到達(假設最後一個ACK丟失,那麼服務器再重發一個FIN。這時雖然客戶端的進程不在了,但是TCP連接還在,仍然可以重發LAST_ACK).

對於服務器上出現大量的CLOSE_WAIT狀態,原因就是服務器沒有正確關閉socket,導致四次揮手沒有正確完成。這是一個bug,只需要加上對應的close即可解決問題。

滑動窗口

提高傳輸效率,批量發送,批量等待ACK
對每一個發送的數據段,都要給一個ACK確認應答。收到ACK後再發送下一個數據段。這樣做有一個比較大的問題。就是性能比較差,尤其是數據往返的時間比較長的時候。
既然這樣一發一收的方式性能比較低,那麼我們一次發送多條數據,就可以大大的提高性能(其實是將多個的等待時間重疊在一起了)。
窗口大小指的是無需等待確認應答而可以繼續發送數據的最大值
收到第一個ACK後,滑動窗口向後移動,繼續發送下一段的數據,以此類推。
操作系統內核爲了維護這個滑動窗口,需要開闢發送緩衝區來記錄當前還有哪些數據沒有應答,只要確認過應答的數據,才能從緩衝區刪掉。(防止出現丟包)
窗口越大,則網絡的吞吐率就越高

  1. 情況一:數據包已經抵達,ACK被丟掉。
    這種情況下,部分ACK丟了並不要緊,因爲可以通過後續的ACK進行確認。後續的ACK確認信息會覆蓋前面ACK的信息。
  2. 情況二:數據包直接丟了
    當某一段報文段丟失之後,發送端會一直收到丟失數據包的序列號,就像是提醒發送端“我要某個序列號”。如果發送端主機連續三次收到了同樣一個"ACK"確認信號的應答,就會將丟失的數據包重新進行發送。這個時候接收端收到了丟失的數據包之後,根據接收端操作系統內核的接收緩衝區中的信息,這種機制被稱爲“高速重發控制”(也叫“快重傳”)
    快速重傳和超時重傳的本質是一樣的,藉助了確認序號的特點,能夠做到只重傳丟了哪一個數據,其他正確到達的數據無需重複傳輸。

窗口越大,傳輸效率越高
窗口不能無限大,傳輸效率建在可靠性上

  1. 發送方如果發送太快,接收方可能除了不過來
    發送方發送的數據先要到達接收方的網卡,然後放到內核的接收緩衝區中。接收方的用戶代碼從接收緩衝區取數據(recv/read)
    限制方法:流量控制->控制窗口大小
  2. 如果當前網絡比較擁堵,發送方如果發送的很快,仍然會丟包
    限制方式:擁塞控制->控制窗口大小
    真實的發送方的窗口大小是流量控制和擁塞控制兩者的最小值。

流量控制

接收端處理數據的速度是有限的。如果發送端發的太快,導致接收端的緩衝區被打滿,這個時候如果發送端繼續發送,就會丟包,繼而引起丟包重傳等等一系列連鎖反應。
因此TCP支持根據接收端的處理能力,來決定發送端的發送速度,這個機制就叫做流量控制

  • 接收端將自己可以接收的緩衝區大小放入TCP首部中的“窗口大小”字段,通過ACK端通知發送端;窗口大小字段越大,說明網絡的吞吐量越高。
  • 接收端一旦發現自己的緩衝區快滿了,就會將窗口大小設置成一個更小的值通知給發送端
  • 發送端接收到這個窗口之後,就會減慢自己的發送速度
  • 如果接受端緩衝區滿了,就會將窗口置爲0,這時發送方不再發送數據,但是需要定期發送一個窗口探測數據端,使接受端把窗口大小告訴給發送端。

擁塞控制

雖然TCP有了滑動窗口這個大殺器,能夠高效可靠的發送大量數據,但是如果在剛開始階段就發送大量的數據,仍然可能引發問題。因爲網絡上有很多的計算機,可能當前的網絡狀態就已經比較擁堵。在不清楚當前網絡狀態下,貿然發送大量的數據,是很有可能引起雪上加霜的。

TCP引入慢啓動機制,先發少量的數據,探探路,摸清當前的網絡擁堵狀態,再決定按照多大的速度傳輸數據。

  • 爲此引入一個概念稱爲擁塞窗口
  • 發送開始的時候,定義擁塞窗口大小爲1
  • 每次收到一個ACK應答,擁塞窗口加1
  • 每次發送數據包的時候,將擁塞窗口和接受端主機反饋的窗口大小做比較,取較小的值作爲實際發送的窗口。

像上面這樣的擁塞窗口增長速度,是指數級別的。“慢啓動”只是指初始時慢,但是增長速度非常快。

  • 爲了不增長的那麼快,因爲不能使用擁塞窗口的單純的加倍。
  • 爲此引入了一個叫做慢啓動的閾值
  • 當擁塞窗口超過這個閾值的時候,不再按照指數方式增長,而是按照線性方式增長
  • 當TCP開始啓動的時候,慢啓動閾值等於窗口最大值
  • 在每次超時重發的時候,慢啓動閾值會變成原來的一半,同時擁塞窗口置爲1

少量的丟包,我們僅僅是觸發超時重傳;大量的丟包,我們認爲網絡擁塞
當TCP通信開始後,網絡吞吐量會逐漸上升,隨着網絡發送擁堵,吞吐量會立刻下降
擁塞控制,歸根到底是TCP協議想儘可能快的把數據傳輸給對方,到那時又要避免給網絡造成太大壓力的折中方案

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章