1萬字30張圖說清TCP協議

0一、簡介

TCP(Transmission Control Protocol 傳輸控制協議)是一種基於IP的傳輸層協議,TCP協議面向鏈接、正面確認與重傳、緩衝機制、流量控制、差錯控制、擁塞控制,可保證高可靠性(數據無丟失、數據無失序、數據無錯誤、數據無重複到達)傳輸層協議。緩存

圖片

上圖形象展現了TCP協議是基於IP協議的傳輸層協議,對於IP協議的詳解,請看《IP協議詳解》。安全

0二、TCP協議頭

TCP協議頭數據個數以下:服務器

圖片

端口號[16bit]網絡

咱們知道,網絡實現的是不一樣主機的進程間通訊。在一個操做系統中,有不少進程,當數據到來時要提交給哪一個進程進行處理呢?這就須要用到端口號。在TCP頭中,有源端口號(SourcePort)和目標端口號(DestinationPort)。源端口號標識了發送主機的進程,目標端口號標識接受方主機的進程。端口是由互聯網分配號碼管理局(IANA)分配的,具體請看《UDP協議詳解》。併發

序號[32bit]app

序號分爲發送序號(SequenceNumber)和確認序號(AcknowledgmentNumber)。socket

發送序號:用來標識從TCP源端向TCP目的端發送的數據字節流,它表示在這個報文段中的第一個數據字節的順序號。若是將字節流看做在兩個應用程序間的單向流動,則 TCP用順序號對每一個字節進行計數。序號是32bit的無符號數,序號到達2∧32- 1後又從 0開始。當創建一個新的鏈接時,SYN標誌變1,順序號字段包含由這個主機選擇的該鏈接的初始順序號ISN(Initial Sequence Number)。tcp

確認序號:包含發送確認的一端所指望收到的下一個順序號。所以,確認序號應當是上次已成功收到數據字節順序號加 1。只有ACK標誌爲1時確認序號字段纔有效。TCP爲應用層提供全雙工服務,這意味數據能在兩個方向上獨立地進行傳輸。所以,鏈接的每一端必須保持每一個方向上的傳輸數據順序號。性能

在wireshark的抓包文件中,Seq表示發送序列號,Ack表示確認序列號。動畫

圖片

偏移[4bit]

這裏的偏移實際指的是TCP首部的長度,它用來代表TCP首部中32bit字的數目,經過它能夠知道一個TCP包它的用戶數據是從哪裏開始的。這個字段佔4bit,如4bit的值是0101,則說明TCP首部長度是5* 4 = 20字節。因此TCP的首部長度最大爲15* 4 = 60字節。然而沒有可選字段,正常長度爲20字節。

圖片

Reserved [3bit]

目前沒有使用,它的值都爲0。注意:在比較舊的資料中顯示6bit的保留字節,由於新的TCP協議使用了3個位做爲標誌,因此只剩下3個保留位。

標誌[9bit]

上面說到增長了3位做標誌位,增長的是:

NS: "nonce sum"簡寫。隨機和,該標籤用來保護不受發送者發送的突發的惡意隱藏報文的侵害。

CWR: "Congestion WindowReduced"簡寫。擁塞窗口減,發送方下降它的發送速率,發送者在接收到一個帶有ECEflag包時,將會使用CWRflag。

ECE: "ECN-Echo"簡寫。ECN表示ExplicitCongestion Notification(顯式擁塞通知),發送方接收到了一個更早的擁塞通告。表示TCPpeer有ECN能力。

其餘6個標誌位

URG: "urgent"簡寫。通知接收端處理在處理其餘包前優先處理接收到的緊急報文(urgentpackets),緊急指針(urgentpointer)有效。

ACK: "Acknowledgment"簡寫。表示包已經被成功接收,確認序號有效。

PSH: "push"簡寫。通知接收端處理接收的報文,而不是將報文緩存到buffer中。

RST:"reset"簡寫。重置鏈接標誌,用於重置因爲主機崩潰或其餘緣由而出現錯誤的鏈接。復位通信請求,通常表示斷開一個鏈接。咱們把含有RST標識的報文稱爲復位報文段。

SYN:"Synchronisation"簡寫。表示三次握手創建鏈接的第一步,在創建鏈接時發送者發送的第一個包中設置flag值爲SYN。咱們把含有SYN標識的報文稱爲同步報文段。

FIN: "finished"簡寫。表示發送者以及發送完數據,一般用在發送者通知對端,本端即將關閉。咱們把含有FIN標識的報文稱爲結束報文段

注意:他們中的多個可同時被置爲1。

圖片

窗口大小(window)[16bit]

指的是接收窗口,窗口的大小,表示源方法最多能接受的字節數。

校驗和[16bit]

校驗和覆蓋了整個的TCP報文段:TCP首部和TCP數據。這是一個強制性的字段,必定是由發端計算和存儲,並由收端進行驗證。

緊急指針[16bit]

只有當URG標誌置爲1時緊急指針纔有效。緊急指針是一個正的偏移量,和序號字段中的值相加表示緊急數據最後一個字節的序號。TCP的緊急方式是發送端向另外一端發送緊急數據的一種方式。

TCP選項

長度不定,但長度必須是32bits的整數倍。TCP頭部的最後一個選項字段(options)是可變長的可選信息。這部分最多包含40字節,由於TCP頭部最長是60字節(其中還包含前面討論的20字節的固定部分)。典型的TCP選項頭部結構如圖所示。

圖片

  1. 選項的第一個字段kind說明選項的類型,有的TCP選項沒有後面兩個字段,僅包含1字節的kind字段。

  2. 第二個字段length(若是有的話)指定該選項的總長度,該長度包括kind字段和length字段佔據的2字節。

  3. 第三個字段info(若是有的話)是選項的具體信息。

常見的TCP選項有7種,如圖所示

圖片

一、kind=0,選項表結束(EOP)選項

一個報文段僅用一次。放在末尾用於填充,用途是說明:首部已經沒有更多的消息,應用數據在下一個32位字開始處。

二、kind=1,空操做(NOP)選項

沒有特殊含義,通常用於將TCP選項的總長度填充爲4字節的整數倍。

三、kind=2,最大報文段長度(MSS)選項

TCP鏈接初始化時,通訊雙方使用該選項來協商最大報文段長度。TCP模塊一般將MSS設置爲(MTU-40)字節(減掉的這40字節包括20字節的TCP頭部和20字節的IP頭部)。這樣攜帶TCP報文段的IP數據報的長度就不會超過MTU(假設TCP頭部和IP頭部都不包含選項字段,而且這也是通常狀況),從而避免本機發生IP分片。對以太網而言,MSS值是1460(1500-40)字節。

四、kind=3,窗口擴大因子選項

TCP鏈接初始化時,通訊雙方使用該選項來協商接收窗口的擴大因子。在TCP的頭部中,接收窗口大小是用16位表示的,故最大爲65535字節,但實際上TCP模塊容許的接收窗口大小遠不止這個數(爲了提升TCP通訊的吞吐量)。窗口擴大因子解決了這個問題。

假設TCP頭部中的接收通告窗口大小是N,窗口擴大因子(移位數)是M,那麼TCP報文段的實際接收通告窗口大小是N*(2^M),或者說N左移M位。注意,M的取值範圍是0~14。咱們能夠經過修改/proc/sys/net/ipv4/tcp_window_scaling內核變量來啓用或關閉窗口擴大因子選項。

和MSS選項同樣,窗口擴大因子選項只能出如今同步報文段中,不然將被忽略。但同步報文段自己不執行窗口擴大操做,即同步報文段頭部的接收窗口大小就是該TCP報文段的實際接收窗口大小。當鏈接創建好以後,每一個數據傳輸方向的窗口擴大因子就固定不變了。

五、kind=4,選擇性確認(SelectiveAcknowledgment,SACK)選項

TCP通訊時,若是某個TCP報文段丟失,則TCP會重傳最後被確認的TCP報文段後續的全部報文段,這樣原先已經正確傳輸的TCP報文段也可能重複發送,從而下降了TCP性能。SACK技術正是爲改善這種狀況而產生的,它使TCP只從新發送丟失的TCP報文段,而不用發送全部未被確認的TCP報文段。選擇性確認選項用在鏈接初始化時,表示是否支持SACK技術。咱們能夠經過修改/proc/sys/net/ipv4/tcp_sack 內核變量來啓用或關閉選擇性確認選項。

六、kind=5,SACK實際工做的選項

該選項的參數告訴發送方本端已經收到並緩存的不連續的數據塊,從而讓發送端能夠據此檢查並重發丟失的數據塊。每一個塊邊沿(edgeofblock)參數包含一個4字節的序號。其中塊左邊沿表示不連續塊的第一個數據的序號,而塊右邊沿則表示不連續塊的最後一個數據的序號的下一個序號。這樣一對參數(塊左邊沿和塊右邊沿)之間的數據是沒有收到的。由於一個塊信息佔用8字節,因此TCP頭部選項中實際上最多能夠包含4個這樣的不連續數據塊(考慮選項類型和長度佔用的2字節)。

七、kind=8,時間戳選項

該選項提供了較爲準確的計算通訊雙方之間的迴路時間(RoundTrip Time,RTT)的方法,從而爲TCP流量控制提供重要信息。咱們能夠經過修改/proc/sys/net/ipv4/tcp_timestamps內核變量來啓用或關閉時間戳選項。

以SYN的TCP選項的MSS爲例的wireshark分析,其餘的你們能夠自行分析。

圖片

整個TCP協議頭部的wireshark解析。

圖片

0三、TCP數據包的編號(SEQ)

一個包1400字節,那麼一次性發送大量數據,就必須分紅多個包。好比,一個10MB的文件,須要發送7100多個包。

發送的時候,TCP協議爲每一個包編號(sequencenumber,簡稱SEQ),以便接收的一方按照順序還原。萬一發生丟包,也能夠知道丟失的是哪個包。

第一個包的編號是一個隨機數。爲了便於理解,這裏就把它稱爲1號包。假定這個包的負載長度是100字節,那麼能夠推算出下一個包的編號應該是101。這就是說,每一個數據包均可以獲得兩個編號:自身的編號,以及下一個包的編號。接收方由此知道,應該按照什麼順序將它們還原成原始文件。

這裏的編號就是TCP頭中的確認號。wireshark顯示的Seq和Ack是wireshark從新編號的。

圖片

數據包1:發送序號:532420307(1),確認序號:2978637660(1)。數據包長6

數據包2:發送序號:2978637660(1),確認序號:532420313(7)。

備註:括號裏是wireshark的編號。

能夠發現:

數據包2的發送序號是數據包1的確認序號。

數據包2的確認序號是數據包1的發送序號+6,也就是加上數據包長。

符合上面的文字描述。

0四、三次握手創建鏈接

三次握手創建鏈接過程:

a.請求端(一般稱爲客戶)發送一個SYN段指明客戶打算鏈接的服務器的端口,以及初始序號(ISN,在這個例子中爲1415531521)。這個SYN段爲報文段1。

b.服務器發回包含服務器的初始序號的SYN報文段(報文段2)做爲應答。同時,將確認序號設置爲客戶的ISN加1以對客戶的SYN報文段進行確認。一個SYN將佔用一個序號。

c.客戶必須將確認序號設置爲服務器的ISN加1以對服務器的SYN報文段進行確認(報文段3)。

這三個報文段完成鏈接的創建。這個過程也稱爲三次握手(three-wayhandshake)。

圖片

用wirshark抓包以下:

圖片

能夠看到三次握手肯定了雙方間包的序號、最大接受數據的大小(window)以及MSS(MaximumSegment Size)。

MSS = MTU - IP頭-TCP頭,MTU表示最大傳輸單元,咱們在IP頭分析的時候會講到,它通常爲1500個字節。IP頭和TCP頭部帶可選選項的時候都是20個字節。這樣的話MSS=1500- 20 -20 = 1460。

MSS限制了TCP包攜帶數據的大小,它的意思就是當應用層向傳輸層提交數據經過TCP協議進行傳輸時,若是應用層的數據大於MSS就必須分段,分紅多個段,逐個的發過去。這部份內容是否是IP分片,不要和IP分片混淆了,IP分片是IP協議層的數據報分片,這是TCP的分片,IP協議分片詳細請看《IP協議詳解》。

咱們wireshar抓包顯示MSS都是1460,這樣顯示不出來握手的協商機制。假設客戶端的MSS是4312,服務器的MSS是1460,那麼握手過程當中的協商能夠下圖形象表示。

圖片

其中,第1 次和第2 次握手包的TCP 首部包含MSS 選項,互相通知對方網絡接口可以適應的MSS 的大小,而後雙方會使用較小的MSS 值進行傳輸。

前面講解TCP頭中flg中就有SYN標誌,在wireshark抓包中也有顯示。

圖片

讀到這裏,好像一切瓜熟蒂落,決定既然互聯網「先驅」定義了三次握手創建,那麼就是三次握手創建鏈接。可有些人會有疑問,爲何兩次握手不能。

好比A給B東西,

A說:我要和你創建,你準備好了嗎?

B說:好的,我準備好了。

A直接把東西給B。

這樣的邏輯在生活中好像一點毛病也沒有,但其實這樣是不行,3次握手完成兩個重要的功能,既要雙方作好發送數據的準備工做(雙方都知道彼此已準備好),也要容許雙方就初始序列號進行協商,這個序列號在握手過程當中被髮送和確認。

如今把三次握手改爲僅須要兩次握手,死鎖是可能發生的。其實上面有個「坑」,那就是一開始咱們限制了A給B東西,但實際的TCP通訊中,鏈接創建了,能夠客戶端主動和服務器通訊,也能夠服務器主動和客戶端通訊,若是兩次握手,B收到A的握手申請,發送好的,我準備好了。這時候B在想,A若是收不到怎麼辦,A到底有沒有收到啊,我(B)能不能向A發數據???

因此須要三次握手。

A說:我和你創建,你準備好了嗎?

B說:好的,我準備好了。

A說:我知道你準備好了(我也準備好了)。

開始愉快的相互傳輸數據。

圖片

0五、四次揮手斷開鏈接

四次揮手斷開鏈接過程:

a.如今的網絡通訊都是基於socket實現的,當客戶端將本身的socket進行關閉時,內核協議棧會向服務器自動發送一個FIN置位的包,請求斷開鏈接。咱們稱首先發起斷開請求的一方稱爲主動斷開方。

b.服務器端收到請客端的FIN斷開請求後,內核協議棧會當即發送一個ACK包做爲應答,表示已經收到客戶端的請求。

c.服務器運行一段時間後,關閉了本身的socket。這個時候內核協議棧會向客戶端發送一個FIN置位的包,請求斷開鏈接。

d.客戶端收到服務端發來的FIN斷開請求後,會發送一個ACK作出應答,表示已經收到服務端的請求。

圖片

用wirshar抓包分析以下:

圖片

前面講解TCP頭中flg中就有FIN標誌,在wireshark抓包中也有顯示。

圖片

下圖類比四次揮手過程:

圖片

這裏有個問題,若是有同窗本身wireshark抓包分析的話(我提供的wireshark文件第一次通訊也是這種狀況),會發現下面狀況:

圖片

怎麼只有3次揮手,應用程序出問題了?wirshark自行」合併「了?爲何別人抓包就有四次揮手斷開?

這跟Wireshark沒有關係,跟實現有關。四次揮手,都知道是客戶端和服務器之間交互的四個報文,FIN、ACK、FIN、ACK。但抓包來看,卻不是每次如教科書說的那樣。首先要搞明白這個FIN報文的真正用途,FIN報文用在本端沒有數據發送給對方時,關閉從本端到對端的鏈接。可是並不影響從對方到本端的鏈接,也就是說本端仍然能夠接收對方的數據。即發送通道關閉,接收通道正常。若是對方收到本端FIN報文時,對方的接收通道就會關閉。此時,若是對方也沒有數據發給本端,那麼對方也會發送FIN給本端,用於關閉從對方到本端的鏈接,這時候就可能出現ACK和FIN合在一塊兒的狀況。固然,若是對方仍然有數據發送,那麼就等數據發完,再發FIN來關閉鏈接,這時候就是四次揮手了。所以,四次揮手變成三次,跟wireshark不要緊,跟數據的收發雙方纔有關係,從這也能看出tcp是雙工通訊了。如今的不少的實現都是合併在一塊兒,三個過程,主要是爲了效率和安全。

 

 

TCP 鏈接必須通過時間2MSL 後才真正釋放掉(2MSL的時間的用意 --- 爲了保證A 發送的最後一個ACK 報文段可以到達B.防止「已失效的鏈接請求報文段」出如今本鏈接中.A在發送完最後一個ACK 報文段後,再通過時間2MSL,就可使本鏈接持續的時間內所產生的全部報文段,都從網絡中消失.這樣就可使下一個新的鏈接中不會出現這種舊的鏈接請求報文段)。

0六、TCP可靠性的保證

TCP採用一種名爲「帶重傳功能的確定確認(positiveacknowledge withretransmission)」的技術做爲提供可靠數據傳輸服務的基礎。這項技術要求接收方收到數據以後向源站回送確認信息ACK。發送方對發出的每一個分組都保存一份記錄,在發送下一個分組以前等待確認信息。發送方還在送出分組的同時啓動一個定時器,並在定時器的定時期滿而確認信息尚未到達的狀況下,重發剛纔發出的分組。

下圖a表示帶重傳功能的確定確認協議傳輸數據的狀況,下圖a表示分組丟失引發超時和重傳。爲了不因爲網絡延遲引發遲到的確認和重複的確認,協議規定在確認信息中稍帶一個分組的序號,使接收方能正確將分組與確認關聯起來。

下圖a能夠看出,雖然網絡具備同時進行雙向通訊的能力,但因爲在接到前一個分組的確認信息以前必須推遲下一個分組的發送,簡單的確定確認協議浪費了大量寶貴的網絡帶寬。爲此, TCP使用滑動窗口的機制來提升網絡吞吐量,同時解決端到端的流量控制。

圖片

0七、滑動窗口技術

TCP的滑動窗口主要有兩個做用,一是提供TCP的可靠性,二是提供TCP的流控特性。同時滑動窗口機制還體現了TCP面向字節流的設計思路。

TCP的Window是一個16bit位字段,它表明的是窗口的字節容量,也就是TCP的標準窗口最大爲2^16-1=65535個字節。另外在TCP的選項字段中還包含了一個TCP窗口擴大因子,option-kind爲3,詳細請看上文。

滑動窗口技術是簡單的帶重傳的確定確認機制的一個更復雜的變形,它容許發送方在等待一個確認信息以前能夠發送多個分組。

因此,TCP的滑動窗口的可靠性也是創建在「確認重傳」基礎上的。

TCP 滑動窗口分爲:發送窗口和接收窗口。

發送方的發送緩存內的數據均可以被分爲4類:

  1. 已發送,已收到ACK

  2. 已發送,未收到ACK

  3. 未發送,但容許發送

  4. 未發送,但不容許發送

其中類型2和3都屬於發送窗口。

接收方的緩存數據分爲3類:

  1. 已接收

  2. 未接收但準備接收

  3. 未接收並且不許備接收

以下圖所示,發送方要發送一個分組序列,滑動窗口協議在分組序列中放置一個固定長度的窗口,而後將窗口內的全部分組都發送出去;當發送方收到對窗口內第一個分組的確認信息時,它能夠向後滑動併發送下一個分組;隨着確認的不斷到達,窗口也在不斷的向後滑動。

圖片

上面的解釋,對於不熟悉滑動窗口的同窗,可能看不太明白。下面將詳細講述一下。

上面講解三次握手創建鏈接時說到,握手過程當中商議了MSS,也就是每一包的數據長度。抓包中也顯示的確是1460字節傳輸的。

圖片

可是1460字節不是整數,不方便咱們快速計算,下面講解將MSS假設爲1000,這樣方面快速理解。

在進行數據傳輸時,若是傳輸的數據比較大(大於1000),就須要拆分爲多個數據包進行發送。TCP協議須要對數據進行確認後,才能夠發送下一個數據包,

圖片

從上圖中能夠看到,發送端每發送一個數據包,都須要獲得接收端的確認應答之後,才能夠發送下一個數據包。這樣一來,就會在等待確認應答包環節浪費時間。爲了不這種狀況,TCP引入了窗口概念。窗口大小指的是不須要等待確認應答包而能夠繼續發送數據包的最大值。

例如,窗口大小爲3,數據包的傳輸如圖所示。

圖片

從上圖中能夠看到,發送端發送第一個數據包(1-1000),沒有等待對應的確認應答包,就繼續發送第二個數據包(1001-2000)和第三個包(2001-3000)。當收到第3個數據包的確認應答包時,會連續發送3個數據包(3001-4000,4001-5000,5001-6000)。當收到第6個數據包的確認應答包時,又會發送3個數據包(6001-7000,7001-8000,8001-9000)。

以這種方式發送,就能夠省去多個數據包(第一、二、四、五、七、8個)的確認應答包時間,從而避免了網絡的吞吐量的下降。

這樣就引出了窗口的概念,窗口大小指的是能夠發送數據包的最大數量。建議讀到這裏,剛纔對窗口不太理解的同窗,向上翻翻,再理解一下滑動窗口的圖示。

那麼,此時窗口就經過滑動的方式,向後移動,確保下一次發送仍然能夠發送窗口大小的數據包。這樣的發送方式被稱爲滑動窗口機制。設置窗口大小爲3,滑動窗口機制原理如圖所示。

圖片

上圖中,每1000 個字節表示一個數據包。發送端同時發送了3個數據包(2001-5000),接收端響應的確認應答包爲「下一個發送4001」,表示接收端成功響應了前兩個數據包,沒有響應最後一個數據包。此時,最後一個數據包要保留在窗口中。

因爲窗口大小爲3,發送端除了最後一個包之外,還能夠繼續發送下兩個數據包(5001-6000和6001-7000)。窗口滑動到7001 處。

 

滑動窗口動畫演示:點擊觀看

 

0八、窗口滑動的數據重發

在進行數據包傳輸時,不免會出現數據丟失狀況。這種狀況通常分爲兩種。

  1. 第一種,若是未使用滑動窗口機制,發送的數據包沒有收到確認應答包,那麼數據都會被重發;若是使用了滑動窗口機制,即便確認應答包丟失,也不會致使數據包重發。

  2. 第二種,發送的數據包丟失,將致使數據包重發。

下面詳細介紹使用滑動窗口機制的兩種狀況。

確認應答包丟失

這種狀況指的是前面發送的數據包沒有收到對應的確認應答。當收到後面數據包的確認應答包,表示前面的數據包已經成功被接收端接收了,發送端不須要從新發送前面的數據包了。如圖所示。

圖片

下面分爲5 部分對上圖進行講解。

1) 發送端第1 次發送數據包:這裏設置的窗口大小爲3,能夠最大發送3 個數據包。發送端同時發送3 個數據包1-1000、1001-2000和2001-3000。

2) 接收端返回確認應答包:接收端接收到這些數據,並給出確認應答包。數據包1-1000 和數據包2001-3000 的確認應答包沒有丟失,可是數據包1001-2000 的確認應答包丟失了。

3) 發送端第2 次發送數據包:發送端收到接收端發來的確認應答包,雖然沒有收到數據包1001-2000 的確認應答包,可是收到了數據包2001-3000 的確認應答包。判斷第一次發送的3 個數據包都成功到達了接收端。再次發送3 個數據包3001-4000、4001-5000和5001-6000。

4) 接收端返回確認應答包:接收端接收到這些數據,並給出確認應答包。數據包3001-4000 和數據包4001-5000 的確認應答包丟失了,可是數據包5001-6000沒有丟失。

5) 發送端第3 次發送數據包:發送端收到接收端發來的確認應答包,查看到數據包5001-6000 收到了確認應答包。判斷第2 次發送的3 個數據包都成功到達了接收端。再次發送3 個數據包6001-7000、7001-8000和8001-9000。

發送數據包丟失

這種狀況指的是發送端發送的部分數據包沒有達到接收端。那麼,若是在接收端收到的數據包,不是本應該要接收的數據包,那麼就會給發送端返回消息,告訴發送端本身應該接收的數據包。

若是發送端連續收到3 次這樣的數據包,就認爲該數據包成功發送到接收端,這時就開始重發該數據包。如圖所示。

圖片

下面分爲7 部分對上圖進行講解。

1) 發送端發送數據包:這裏窗口大小爲4,發送端發送4 個數據包,分別爲1-1000、1001-2000、2001-3000和3001-4000。

2) 接收端返回確認應答包:接收端接收到這些數據,並給出確認應答包。接收端收到了數據包1-1000,返回了確認應答包;收到了數據包1001-2000,返回了確認應答包;可是數據包2001-3000,在發送過程當中丟失了,沒有成功到達接收端。數據包3001-4000 沒有丟失,成功到達了接收端,可是該數據包不是接收端應該接收的數據包,數據包2001-3000 纔是真正應該接收的數據包。所以收到數據包3001-4000 之後,接收端第一次返回下一個應該發送2001 的數據包的確認應答包。

3) 發送端發送數據包:發送端仍然繼續向接收端發送4 個數據包,分別爲4001-5000、5001-6000、6001-7000和7001-8000。

4) 接收端返回確認應答包:接收端接收到這些數據,並給出確認應答包。當接收端收到數據包4001-5000 時,發現不是本身應該接收的數據包2001-3000,第二次返回下一個應該發送2001 的數據包的確認應答包。當接收端收到數據包5001-6000 時,仍然發現不是本身應該接收的數據包2001-3000,第三次返回下一個應該發送2001的數據包的確認應答包。以此類推直到接收完全部數據包,接收端都返回下一個應該發送2001 的數據包的確認應答包。

5) 發送端重發數據包:發送端連續3 次收到接收端發來的下一個應該發送2001 的數據包的確認應答包,認爲數據包2001-3000 丟失了,就進行重發該數據包。

6) 接收端收到重發數據包:接收端收到重發數據包之後,查看此次是本身應該接收的數據包2001-3000,並返回確認應答包,告訴發送端,下一個該接收8001 的數據包了。

7) 發送端發送數據包:發送端收到確認應答包後,繼續發送窗口大小爲4 的數據包,分別爲8001-9000、9001-10000、10001-11000和11001-12000。

0九、TCP流控制

在使用滑動窗口機制進行數據傳輸時,發送方根據實際狀況發送數據包,接收端接收數據包。可是,接收端處理數據包的能力是不一樣的。

1)若是窗口太小,發送端發送少許的數據包,接收端很快就處理了,而且還能處理更多的數據包。這樣,當傳輸比較大的數據時須要不停地等待發送方,形成很大的延遲。

2)若是窗口過大,發送端發送大量的數據包,而接收端處理不了這麼多的數據包,這樣,就會堵塞鏈路。若是丟棄這些本應該接收的數據包,又會觸發重發機制。

3) 爲了不這種現象的發生,TCP提供了流控制。所謂的流控制就是使用不一樣的窗口大小發送數據包。發送端第一次以窗口大小(該窗口大小是根據鏈路帶寬的大小來決定的)發送數據包,接收端接收這些數據包,並返回確認應答包,告訴發送端本身下次但願收到的數據包是多少(新的窗口大小),發送端收到確認應答包之後,將以該窗口大小進行發送數據包。

TCP 流控制過程如圖所示。

圖片

爲了方便講解,將上圖以發送端發送數據包進行分隔,將其分爲3 部分進行講解。

第一部分

發送端根據當前鏈路帶寬大小決定發送數據包的窗口大小。這裏,窗口大小爲3,表示能夠發送3 個數據包。所以發送端發送了3 個數據包,分別爲1-1000、1001-2000和2001-3000。

接收端接收這些數據包,可是隻能處理2 個數據包,第3 個數據包2001-3000 沒有被處理。所以返回確認應答包,設置窗口大小爲2,告訴發送端本身如今只能處理2個數據包,下一次請發送2 個數據包。

第二部分

發送端接收到確認應答包,查看到接收端返回窗口大小爲2,知道接收端只處理了2個數據包。發過去的第3 個數據包2001-3000 沒有被處理。這說明此時接收端只能處理2 個數據包,第3 個數據包還須要從新發送。

所以發送端發送2 個數據包2001-3000 和3001-4000。接收端收到這兩個數據包並進行了處理。此時,仍是隻能處理2 個窗口,繼續向發送端發送確認應答包,設置窗口爲2,告訴發送端,下一個應該接收4001 的數據包。

第三部分

發送端接收到確認應答包,查看到接收端返回窗口大小爲2。說明接收端接收了上次發送的2 個數據包。此時仍然能夠處理2 個數據包,繼續發送數據包4001-5000 和5001-6000。

若是在接收端返回的確認應答包中,窗口設置爲0,則表示如今不能接收任何數據。這時,發送端將不會再發送數據包,只有等待接收端發送窗口更新通知才能夠繼續發送數據包。

若是這個更新通知在傳輸中丟失了,那麼就可能致使沒法繼續通訊。爲了不這樣的狀況發生,發送端會時不時地發送窗口探測包,該包僅有1個字節,用來獲取最新的窗口大小的信息。

原理如圖所示。

圖片

下面介紹上圖所示的獲取窗口更新數據包的原理。

1) 發送端發送數據。發送端以窗口大小爲2,發送了2 個數據包,分別爲4001-5000和5001-6000。接收端接收到這些數據之後,緩衝區滿了,沒法再處理數據,因而向發送端返回確認應答包,告訴它下一個接收6001 的數據,可是如今處理不了數據,先暫停發送數據,設置窗口大小爲0。

2) 發送端暫停發送數據。發送端收到確認應答包,查看到下一次發送的是6001 的數據,但窗口大小爲0,得知接收端此時沒法處理數據。此時,不進行發送數據,進入等待狀態。

3)接收端發送窗口大小更新包。當接收端處理完髮送端以前發來的數據包之後,將會給發送端發送一個窗口大小更新包,告訴它,此時能夠發送的數據包的數量。這裏設置窗口大小爲3,表示此時能夠處理3 個數據包,可是該數據包丟失了,沒有發送到發送端。

4) 發送端發送窗口探測包。因爲窗口大小更新包丟失,發送端的等待時間超過了重發超時時間。此時,發送端向接收端發送一個窗口探測包,大小爲1 字節,這裏是6001。

5) 接收端再次發送窗口大小更新包。接收端收到發送端發來的探測包,再次發送窗口大小更新包,窗口大小爲3。

6) 發送端發送數據。發送端接收到窗口大小更新包,查看到應該發的是6001 的數據包,窗口大小爲3,能夠發送3 個數據包。所以發送了數據包,分別爲6001-7000、7001-8000和8001-9000。

十、網線「斷」了怎麼辦

對於TCP連接來講,他們之間一旦創建了鏈接,那麼能夠一直沒有消息通信。TCP鏈接的雙方都沒有向對方發送數據,則在兩個TCP模塊之間不交換任何信息。

只要兩端的主機沒有被重啓,則鏈接依然保持創建,無論中間路由器能夠崩潰和重啓,仍是電話線被掛斷再連通。這意味着咱們能夠啓動一個客戶與服務器創建一個鏈接,而後離去數小時、數天、數個星期或者數月,而鏈接依然保持。

這對於客戶端來講,倒還好一點,畢竟不會有那麼多的鏈接被佔用,對於服務器來講,就是一個很糟糕的事情,這種鏈接無疑是一種殭屍鏈接,平白無辜的佔用着服務器的資源,一旦這種鏈接很是多,服務器每每會由於鏈接數量的限制,致使沒有辦法接入新的客戶端。

這個時候,其實就須要一種定時探測對端鏈接是否還存活的機制存在,如此以來彼此都能知道對方的狀態,是否還能繼續使用。

這種機制,對於TCP來講,就是TCP的保活機制。TCP還設有一個保活計時器,服務器每收到一次客戶端的請求後都會從新復位這個計時器,時間一般是設置爲2小時,若兩小時尚未收到客戶端的任何數據,服務器就會發送一個探測報文段,之後每隔75秒鐘發送一次,俗稱「心跳」。若一連發送10個探測報文仍然沒反應,服務器就認爲客戶端出了故障,接着就關閉鏈接。

TCP具備保活器,但我建議在應用層最好還要設計一個「心跳」用來維持TCP鏈接,時間間隔可自行肯定。再插一嘴,具備保活器的TCP就是長鏈接。

 

長鏈接:創建一個鏈接,多個請求複用這個鏈接,一直用同一個連接傳輸數據,最後再關閉鏈接。

短鏈接:創建一個鏈接,傳輸一個請求,發送完數據後就關閉鏈接。

TCP具備保活器優勢:

1.在鏈接兩個端系統的網絡出現臨時故障的時候,保活選項會引發一個 實際上很好的鏈接終止。例如,若是在一箇中間路由器崩潰並從新啓動時發送保活探查,那麼TCP會認爲客戶的主機已經崩潰,而實際上所發生的並不是如此。

2.保活功能主要是爲服務器應用程序提供的。服務器應用程序但願知道客戶主機是否崩潰,從而能夠表明客戶使用資源,及時回收這些資源。

TCP具備保活器缺點:

保活並非TCP規範中的一部分。HostRequirements RFC提供了3個不使用保活定時器的理由:

1)在出現短暫差錯的狀況下,這可能會使一個很是好的鏈接釋放掉;

2)它們耗費沒必要要的帶寬;

3)在按分組計費的狀況下會在互聯網上花掉更多的錢。

 

wireshark抓包文件:

連接:https://pan.baidu.com/s/1AYU7lrbjE5zaBdhb76irqg   提取碼:yxbf 

 

點擊查看本文所在的專輯,STM32F207網絡開發

相關文章
相關標籤/搜索