目錄
一、TCP協議段格式
二、TCP原理
1.確認應答
?2.超時重傳
3.連接管理
建立連接
斷開連接
4.滑動窗口
5.流量控制
6.擁塞控制
?7.延時應答
8.捎帶應答
?9.面向字節流
10.TCP異常情況
TCP,即Transmission Control Protocol,傳輸控制協議。人如其名,要對數據的傳輸進行一個詳細的控制。
一、TCP協議段格式
TCP段 = 報頭(首部)+ 載荷(數據)
TCP報頭分為了 11個部分,報頭的最小長度是20字節,是不包含選項部分的,包含選項部分是60字節:
- ?源/目的端口號:表示數據是從哪個進程來,到哪個進程去。
- 32位序號/32位確認號:后面會詳細講。
- 4位TCP首部長度:有4位(4bit),此處的單位是4字節,4位二進制表示最大的數是15,所以4位首部長度能表示TCP頭部最大長度是15*4=60。
- 6位標志位:
- URG:緊急指針是否有效。
- ACK:確認號是否有效。
- PSH:提示接收端應用程序立刻從TCP緩沖區把數據讀走。
- RST:對方要求重新建立連接;我們把攜帶RST標識的稱為復位報文段。
- SYN:請求建立連接;我們把攜帶SYN標識的稱為同步報文段。
- FIN:通知對方,本端要關閉了,我們稱攜帶FIN標識的為結束報文段。
- 窗口大小:后面會詳細講到。
- 16位校驗和:發送端填充,CRC校驗。接收端校驗不通過,則認為數據有問題。此處的校驗和不光包含TCP首部,也包含TCP數據部分。
- 16位緊急指針:標識哪部分數據是緊急數據。
- 選項:可選的,可以有也可以沒有,也是頭部的一部分
- 保留6位:由于UDP的長度最大為64kb,改不了,所以在TCP段里面這個保留位就留下了擴展的余地,但這個部分暫時用不到,后面如果有需要會再使用。
二、TCP原理
在學習TCP原理之前,先了解一下TCP的特點:
- 有連接
- 可靠傳輸
- 面向字節流
- 有接收緩沖區,也有發送緩沖區
- 大小不限
- 是全雙工
其中可靠傳輸時TCP最核心的特性。
TCP對數據傳輸提供的管控機制,主要體現在兩個方面:可靠和效率。
這些機制和多線程的設計原則類似:保證數據的安全可靠傳輸的前提下,盡可能的提高效率。
其中,保證可靠性需要進行下面操作:
- 校驗和
- 序列號
- 確認應答
- 超時重發
- 連接管理
- 流量控制
- 擁塞控制
提高性能進行下面操作:
- 滑動窗口
- 快速重傳
- 延遲應答
- 捎帶應答
1.確認應答
發送方,把數據發給接收方之后,接收方收到數據就會給發送方返回一個應答報文(acknowledge,ack),如果發 送方收到這個應答報文了,就知道自己的數據是否發送成功了。
我們先了解一下序號和確認號:
- 序號:標識發送方發送的數據段的起始字節。
- 確認號:標識接收方期望接收的下一個字節的序號。
TCP是面向字節流的,TCP將每個字節的數據都進行了編號,即為序列號。如下圖:
TCP段傳輸中確認應答的流程:
?補充:區分一個數據包是普通的數據還是ACK應答數據看ACK,當ACK這一位為1,表示當前數據包是一個應答報文,此時該數據包中的“確認序號字段”就能生效,反之為0,表示當前是一個普通報文,此時數據包中的“確認序號字段”是不生效的。
?2.超時重傳
上面的確認應答描述的是一個比較理想的情況,如果網絡傳輸過程中,出現丟包了,那么發送方勢必就無法收到ACK了,下面是使用超時重傳機制針對確認應答進行補充。
補充:
丟包是一個隨機的事件,因此在上述tcp傳輸過程中,丟包就存在兩種情況:
- 傳輸的數據丟了
- 返回的ack丟了
站在發送方的角度是無法區分這兩種情況的,本質上是發送方收不到ACK了。
?出現丟包的情況,發送方都會進行重新傳輸。
因此接收方會收到很多重復數據,TCP會有一個“接收緩沖區”,就是一個內存空間,會保存當前已經收到的數據以及數據的序列號,接收方如果發現當前發送方發來的數據是已經在接收緩沖區中存在的,接收方就會直接把這個后來的數據給丟掉,確保應用程序進行read的時候讀到的只有一條數據。
補充:接收緩沖區不僅僅能進行去重,確保發送的順序和應用程序讀取的順序是一致的。
?發送方對于重傳,那超時的時間(即等待的時間)如何確定的呢?
- 最理想的情況下,找到一個最小的時間,保證“確認應答一定能在這個時間內返回”,如果超過會下一次重傳;
- 但是這個時間的長短,隨著網絡環境的不同,是有差異的;
- 如果超過時間設的太長,會影響整體的重送效率;
- 如果超過時間設的太短,有可能頻繁發送重復的包;
????????TCP為了保證無論在任何環境下都能比較高性能的通信,初始的等待時間是可配置的,不同系統上都不一定一樣,也可以通過修改內核參數來引起這里的時間變化;當然,等待的時間也是會動態變化的,每多經歷一次超時,等待的時間都會變長,比如第一次重傳,如果發送方還是沒有收到,那么第二次重傳后的等待時間會比第一次等待時間更長。
3.連接管理
連接管理包括了建立連接和斷開連接,分別對應了我們平時所說的三次握手和四次揮手。在正常情況下,TCP要經過三次握手建立連接,四次揮手斷開連接。
連接管理的整個流程:
建立連接
這里的建立連接,就是給對方傳輸一個簡短的,沒有業務數據的數據包,通過這個數據包來喚起對方的注意從而觸發后續的操作。
TCP在建立連接的過程中需要通信雙方一共“打三次招呼”才能夠完成連接的建立。
說明:三次握手的過程中,涉及了兩個報段,一個是ack應答報文,另一個是syn同步報文段兩者都不攜帶業務數據。
三次握手流程:
補充:
- CLOSED:表示沒有建立連接或連接關閉。
- SYN_SENT:客戶端發送SYN包來發起連接。
- ESTABLISHED:建立連接,可以開始數據傳輸。
- SYN_RCYD:服務器收到客戶端的SYN包。
為什么中間的那段ACK和SYN不可以分開發送然后變成四次握手呢?
兩個數據合并在一起發送這樣會使效率更高。
三次握手的作用:
確認當前網絡是否暢通;發送方和接收方的發送和接收是否均正常;讓通信雙方在握手中針對一些重要參數進行協商。
斷開連接
四次揮手用于在客戶端和服務器之間終止TCP連接。這個過程確保雙方都完成數據傳輸并安全關閉連接。
說明:四次揮手的過程中涉及了FIN結束報文段和ACK應答報文段這兩段報文。
四次揮手流程:
?補充:
- FIN_WAIT_1:客戶端發送FIN包以關閉連接。
- FIN_WAIT_2:客戶端等待結束報文段。
- CLOSE_WAIT:服務器準備關閉連接。
- LAST_ACK:服務器準備接收客戶端發來的ACK。
- TIME_WAIT:客戶端發送ACK后等待一段時間后關閉,等待一個2MSL(Max Segment Life ,報文最大生存時間)。
- CLOSED:關閉連接。
- 和三次握手不同,此處的四次揮手是否能夠把中間的兩次交互合二為一?
因為ACK和第二個FIN的觸發時機是不同的,ACK是內核響應的,B收到FIN就會立即返回ACK,第二個FIN是應用程序代碼觸發的,B這邊調用了close方法才會觸發FIN。從服務器收到FIN再到執行close方法然后發起FIN這中間要經歷多少時間經歷多少代碼是不確定的,所以不能合并。
但是如果利用TCP的機制延時應答(后面會涉及)拖延一下ACK應答時間就有可能和FIN合并在一起。
- 最后一個TIME_WAIT的作用是如果最后一個ACK丟了,B就會觸發超時重傳,重新發送FIN,那么服務端有了TIME_WAIT這個狀態就能夠收到重新發送的信息了。如果沒有TIME_WAIT,客戶端就會釋放連接了,服務端發送的FIN就無法處理了,那么也無法收到ACK了。
4.滑動窗口
剛才上面討論了確認應答策略,對每一個發送的數據段都要給一個ACK確認應答。收到ACK后再發送下一個數據段。這樣做有一個比較大的缺點,就是性能較差。尤其是數據往返的時間較長的時候。
每次收到一個應答報文再發下一個數據,這個過程中性能是比較低的,那么我們可以考慮一次性發送多條數據,就可以大大的提高性能(其實是將多個段的等待時間重疊在一起了)。
?其中批量傳輸也不是“無限”的傳輸,批量傳輸也是存在一定的上限的達到一定上限之后再統一等待ack,其中這個上限的最大數據量稱為“窗口大小”。
- 這個窗口大小指的是無需等待確認應答而可以繼續發送數據的最大值。上圖的窗口大小就是4000個字節(四個字段)。
- 發送前四個段的時候,不需要等待任何ACK,直接發送。
- 收到第一個ACK后,滑動窗口向后移動,繼續發送第五個段的數據,依次類推。
- 操作系統內核為了維護這個滑動窗口,需要開辟發送緩沖區來記錄當前還有哪些數據沒有應答;只有確認應答的過的數據才能從緩沖區刪除掉。
- 窗口越大,則網絡的吞吐率就越高。
如圖(發送方的滑動窗口):
那么如果出現了丟包,如何進行重傳呢?
情況一:數據包已經抵達,部分ACK卻丟了
像這種情況下不需要任何重傳的,因為TCP段的確認序號(每個字節的序號)的含義是當前之前的數據已經確認收到了,其采用的是累計確認機制,每一次接收數據包都會把下一個數據段的序號存到確認序號里面請求下一個數據段,當發送確認號為1001的ack=1丟失時,下一個確認號為2001的ack=1發送成功后就能夠證明1-1000段的數據接收成功無需再次發送,這么理解就是主機B只有確認或請求下一段數據段就能夠說明之前的就已經是收到的了。
情況二:數據包丟了
當1001~2000的這段數據段丟失時:雖然3001~4000,4001~5000,5001~6000,6001~7000都被接收方收到了(放到了接收緩沖區里,同時接收方會檢查有沒有缺少的數據段),但是接收方一直在等待和請求序號為1001開頭的數據段,反復發送ACK號是1001的確認應答請求,當發送端收到幾個同樣的確認應答后進行重發,接收端就會發送一個新的ACK,確認1~7000字節的數據已經成功接收,ACK號是7001,表示接收方期望收到下一段從7001開始,這樣子1~7000的內容就表明都收到了。
補充:
- 為什么不把未發送的ack都發送了?
ack是累計確認機制,序號最大的發送了ack就表明了前面的已經收到了,同時少發ack能夠減少網絡擁堵,提高網絡傳輸效率。
- 快速重傳機制:
依賴于接收方發送重復的ACK(相同的確認號),在這個機制下,發送方收到幾次相同的ACK后會認為某個數據包丟失并進行重傳。
- 如果通信雙方傳輸的數據量比較小,也不頻繁,就仍然是普通的確認應答和普通的超時重傳;如果通信雙方傳輸數據量大也比較頻繁,就會進入到滑動窗口模式,按照快速重傳的方式處理。
- 滑動窗口傳輸數據效率是會提升的,窗口越大,傳輸效率越大。
- 滑動窗口越大越好嗎?
如果傳輸速度太快就可能使接收方處理不過來,此時,接收方也會出現丟包,發送方還得重傳。
5.流量控制
由于接收端處理數據的速度是有限的。如果發送端發的太快,導致接收端的緩沖區被填滿,這個時候如果發送端繼續發送,就會造成丟包,繼而引起丟包重傳等一系列連鎖反應。
因此TCP支持根據接收端的處理能力來決定發送端的發送速度。這個機制就叫做流量控制(Flow
Control);
在網絡協議中,尤其是在 TCP(傳輸控制協議) 中,滑動窗口機制既在 發送端 起作用,也在 接收端 起作用,它的作用是為了控制數據的流量和保證數據的可靠傳輸。
-
發送端: 發送方維護一個滑動窗口,表示在某一時間內可以發送的數據量。滑動窗口的大小通常取決于接收方的緩沖區大小以及網絡的擁塞狀況。發送方會根據接收方的反饋來調整滑動窗口的大小,以保證不會發送超過接收方能夠處理的數據量。
-
接收端: 接收方也會維護一個滑動窗口,用來告訴發送方,它的接收緩沖區有多少空間。這就可以避免發送方向接收方發送過多的數據,導致接收方無法處理而發生丟包等問題。
?流量控制機制:
- 接收端將自己可以接收的緩沖區大小放入TCP首部中的“16位窗口大小”字段,即表示接收端緩沖區剩余空間的大小,通過返回ACK的形式通知發送端調整發送速度;
- 窗口大小字段越大,說明網絡的吞吐量越高;
- 接收端一旦發現自己的緩沖區快滿了,就會將窗口大小設置成一個更小的值然后通知給發送端;
- 發送端接收到這個窗口大小之后,就會減慢自己的發送速度;
- 如果接收端緩沖區滿了,就會將窗口設置為0,這時發送方不再發送數據,但是需要定期發送一個窗口探測數據段,使接收端把窗口大小通知給發送端。
流量控制流程圖:
補充:
16位數字最大表示65535,那么TCP窗口最大就是65535字節嗎?
實際上,TCP首部選項的40字節中還包含了一個窗口擴大因子M,實際窗口大小是窗口字段的值左移M位。
6.擁塞控制
?雖然TCP有了滑動窗口從而能夠高效可靠的發送大量的數據。但是如果在剛開始階段就發送大量的數據,仍然可能引發問題。因為網絡上有很多計算機,有可能當前的網絡狀態就已經比較擁堵了,在不清楚當前網絡狀態下,貿然發送大量數據,是很有可能引起雪上加霜的情況的。
TCP引入了另一種控制機制:擁塞窗口,用于避免網絡擁塞。它主要控制發送方在網絡中發送數據的速率,以免網絡過載。
擁塞控制機制:
- 擁塞控制的關鍵在于引入了一個擁塞窗口;
- 發送開始的時候定義擁塞窗口大小為1;
- 每次收到一個ACK應答,擁塞窗口加1;
- 每次發送數據包的時候,將擁塞窗口和接收端主機反饋的窗口大小做比較,取較小的值作為實際發送的窗口。
如下圖:
像這樣的擁塞窗口增長速度,是指數級別的。“慢啟動”只是指初始時慢,但是增長速度非常快。
- 為了不增長的那么快,因此不能使擁塞窗口單純的加倍。
- 此處引入一個叫做慢啟動閾值(ssthresh)。
- 當擁塞窗口超過這個閾值的時候,不再按照指數方式增長,而是按照線性方式增長。
如下圖:
- 當TCP開始啟動的時候,慢啟動閾值等于窗口最大值;
- 在每次超時重發的時候,慢啟動閾值會變成原來的一半,同時擁塞窗口重置為 1(丟包現象可能是由擁塞引起的,而少量的丟包僅僅是出發超時重傳,大量的丟包我們就認為是網絡擁塞)。
- 當TCP通信后,網絡吞吐量會逐漸上升;隨著網絡發生擁堵,吞吐量會立刻下降。
補充:
滑動窗口和擁塞窗口的區別與聯系:
- 區別:
滑動窗口主要是由接收方控制,用來告訴發送方它的接收緩沖區中還能接收多少數據。
擁塞窗口是由發送方控制,用來避免網絡中的擁塞,它決定了發送方在網絡中可以發送的最大數據量。
- 聯系:
發送方實際發送的數據量是由這兩個窗口的最小值來決定的:實際發送窗口 =min(滑動窗口,擁塞窗口)。
兩者關注的對象不同卻相互影響。
流量控制與擁塞控制:
兩者都是在限制發送方的發送窗口的大小。
?7.延時應答
如果接收數據的主機立刻返回ACK應答,這時候返回的16位窗口大小可能比較小。
延時應答原理理解:
假設接收端緩沖區為1M,一次收到了500k的數據,如果立刻應答,返回的窗口大小就是500K;
但實際上可能處理端處理的速度很快,10ms之內就把500k數據從接收緩沖區消費掉了;
在這種情況下,接收端處理還遠沒有達到自己的極限,即使窗口大小再放大一點,也能處理過來;
如果接收端稍微等一會再應答返回ACK,比如等待200ms再應答,那么這個時候返回的窗口大小就是1M了。
一定要記得,窗口越大,網絡吞吐量越大,傳輸效率就越高。我們的目標是在保證網絡不擁塞的情況下盡量地提高效率。
那么所有的包都可以延遲應答么?當然不是啦
- 數量限制:會每隔N個包就應答一次;
- 時間限制:超過最大延遲時間就應答一次;
如果所有包都延遲應答,那么傳輸的效率會大大下降的。
具體的數量和超過時間,依照操作系統不同也有差異;一般N取2,超過時間取200ms。
延時應答搭配上流量控制,控制發送端的發送速度,提高了數據傳輸的效率。
8.捎帶應答
在TCP協議中,捎帶應答是指在一個數據包的傳輸過程中,當接收方有數據需要發送回發送方時,它可以將確認應答ACK與數據一起捎帶在同一包中,當然了這種捎帶應答機制存在于發送方和接收方。這種機制優化了網絡的效率,減少了不必要的網絡傳輸。
捎帶應答流程圖:
?9.面向字節流
面向字節流這個機制中,有一個最重要的問題,粘包問題。
此處的“包”是指應用層的數據包,如果發送方頻繁發送小數據包時,接收方無法正確地分辨每個獨立的數據包邊界,此時就容易出現粘包問題。簡單來說,粘包問題指的是多個小的數據包在網絡中“粘在一起”,導致接收方無法明確分開每一個包的數據。
?如圖:
目前接收緩沖區中這三個應用層數據包的數據,就是以字節的形式緊緊挨在一起的,接收方的應用程序讀取數據的時候,可以一次讀一個字節,也可以讀兩個字節,也可以讀N個字節,但是最終的目標是為了得到完整的應用層數據包。對于接收方來說讀多少個字節才是一個完整的數據包,這時不知道的。
TCP數據段與UDP數據報相比,UDP是面向數據報的通信方式,就沒有上述問題。
?解決粘包問題:
思路:通過定義好應用層協議,明確應用層數據包之間的邊界。
1.引入分隔符
?這個時候應用程序讀取數據的時候就可以一直持續讀取數據,直到讀到\n為止。
2.引入長度
應用程序讀數據的時候就可以先讀兩個字節得到數據包的長度,在根據 長度繼續讀取對應字節的個數。
10.TCP異常情況
如果在使用tcp過程中出現意外會如何處理呢?
1)進程崩潰
進程沒了,異常終止了,文件描述表也就釋放了,相當于調用了socket.close()。
此時就會觸發FIN,對方收到之后,自然就會返回FIN和ACK,這邊再進行ACK(四次揮手流程)。
TCP的連接,可以獨立于進程存在(進程沒了,TCP連接不一定沒)
進程斷開后,tcp連接還是存在的,而且是在四次揮手的過程,等到四次揮手結束之后,連接就斷開了。
2)主機關機
在進行關機的時候,就是會先觸發強制終止進程操作(相當于與第一種),此時就會觸發FIN,對方收到之后,自然就會返回FIN和ACK。不僅僅是整個進程沒了,整個系統也可能關閉了,如果在系統關閉之前,對端返回的ACK和FIN到了,此時系統還是可以返回ACK,進行正常的四次揮手。如果系統已經關閉了,對端發來的ACK和FIN遲到了,無法進行后續ACK的響應,這種情況站在對端的角度,對端以為是自己的FIN丟包了,重傳FIN,但是重傳幾次都沒有響應,自然就會放棄連接,把持有對端的信息刪了。
3)主機沒電而關機(非正常)
此時這是一瞬間的事情,來不及殺進程也來不及發送FIN主機直接就停機了。
站在對端的角度,對端不一定知道這個事情,從發送方和接收方兩個角度去看:
1.接收方掉電
如果對端是在發送數據,發送的數據就會一直等待ACK,觸發超時重傳,觸發TCP連接重置功能,發起“復位報文段(RST)”,如果復位報文段發過去之后也沒有效果此時就會釋放連接了。
2.發送方掉電
如果對端在接收數據,對端還在等待等了半天沒有消息,此時其實無法區分是對端沒發消息還是對方掉線了。
TCP中提供了心跳包機制:接收方也會周期性的給發送方發起一個特殊的不攜帶業務數據的數據包,并且期望對方返回一個應答,如果對方沒有應答,并且重復了多次之后仍然沒有應答就視為對方掉線了,此時就可以單方面的釋放連接了。
?4)網線斷開
網線斷開和剛才的主機掉電這兩種情況一樣的。
好啦,這些是TCP協議的內容講解,可能會有些不足,感謝閱讀!!!