關於TCP協議的總結

昨天下班在地鐵上看到一篇關於TCP總結的博文,覺得非常好,這裏給借鑑過來,由於原文裏有一些關於TCP協議部分晦澀難懂的部分,這裏就不照搬過來了,只摘抄一些大家熟知的部分,例如三次握手,滑動窗口等。原文地址:朱小廝的博客

三次握手

幾個名詞:

SYN:處於TCP連接建立過程

ACK:確認序號標誌,爲1時表示確認號有效,爲0時表示報文中不含確認信息,忽略該字段。

連接過程

1.TCP服務器進程打開,準備接受客戶進程的連接請求,此時服務器進入了LISTEN(監聽)狀態

2.客戶端向服務器發出連接請求報文,這時報文首部SYN=1,同時選擇一個初始序列號 seq=x ,此時,TCP客戶端進程進入了 SYN-SENT(同步已發送狀態)狀態。TCP規定,SYN報文段(SYN=1的報文段)不能攜帶數據,但需要消耗掉一個序號。

3.TCP客戶進程收到確認後,還要向服務器給出確認。確認報文的ACK=1,ack=y+1,自己的序列號seq=x+1,此時,TCP連接建立,客戶端進入ESTABLISHED(已建立連接)狀態。TCP規定,ACK報文段可以攜帶數據,但是如果不攜帶數據則不消耗序號。

4.當服務器收到客戶端的確認後也進入ESTABLISHED狀態,雙方可以開始通信。

三次握手主要目的是:防止超時導致髒連接。如果使用的是兩次握手建立連接,假設有這樣一種場景,客戶端發送了第一個請求連接並且沒有丟失,只是因爲在網絡結點中滯留的時間太長了,由於TCP的客戶端遲遲沒有收到確認報文,以爲服務器沒有收到,此時重新向服務器發送這條報文,此後客戶端和服務器經過兩次握手完成連接,傳輸數據,然後關閉連接。此時此前滯留的那一次請求連接,網絡通暢了到達了服務器,這個報文本該是失效的,但是,兩次握手的機制將會讓客戶端和服務器再次建立連接,這將導致不必要的錯誤和資源的浪費。如果採用的是三次握手,就算是那一次失效的報文傳送過來了,服務端接受到了那條失效報文並且回覆了確認報文,但是客戶端不會再次發出確認。由於服務器收不到確認,就知道客戶端並沒有請求連接。

四次揮手

幾個名詞:

FIN:發送端已完成數據傳輸,請求釋放連接。

TIME_WAIT:主動要求關閉的機器表示收到了對方的FIN報文,併發送出了ACK報文,進入TIME_WAIT狀態,等2MSL後即可進入到CLOSED狀態。如果FIN_WAIT_1狀態下,同時收到待FIN標識和ACK標識的報文時,可以直接進入TIME_WAIT狀態,而無需經過FIN_WAIT_2狀態。

CLOSE_WAIT:被動關閉的機器收到對方請求關閉連接的FIN報文,在第一次ACK應答後,馬上進入CLOSE_WAIT狀態。這種狀態其實標識在等待關閉,並且通知應用發送剩餘數據,處理現場信息,關閉相關資源。

釋放過程

1.數據傳輸完畢,客戶端進程發出連接釋放報文,並且停止發送數據。釋放數據報文首部,FIN=1,其序列號爲seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即使不攜帶數據,也要消耗一個序號。

2.服務器收到連接釋放報文,發出確認報文,ACK=1,ack=u+1,並且帶上自己的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。

3.客戶端收到服務器的確認請求後,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送連接釋放報文。

4.服務器將最後的數據發送完畢後,就向客戶端發送連接釋放報文,FIN=1,ack=u+1,由於在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號爲seq=w,此時,服務器就進入了LAST-ACK(最後確認)狀態,等待客戶端的確認。

5.客戶端收到服務器的連接釋放報文後,必須發出確認,ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP連接還沒有釋放,必須經過2MSL(最長報文段壽命)的時間後,才進入CLOSED狀態。

6.服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態

爲什麼客戶端最後還要等待2MSL?

MSL(Maximum Segment Lifetime,TCP允許不同的實現可以設置不同的MSL值)。第一,保證客戶端發送的最後一個ACK報文能夠到達服務器,因爲這個ACK報文可能丟失。站在服務器的角度看來,我已經發送了FIN+ACK報文請求斷開了,客戶端還沒有給我回應,應該是我發送的請求斷開報文它沒有收到,於是服務器又會重新發送一次,而客戶端就能在這個2MSL時間段內收到這個重傳的報文,接着給出迴應報文,並且會重啓2MSL計時器。如果客戶端收到服務端的FIN+ACK報文後,發送一個ACK給服務端之後就“自私”地立馬進入CLOSED狀態,可能會導致服務端無法確認收到最後的ACK指令,也就無法進入CLOSED狀態。第二,防止失效請求。防止類似與“三次握手”中提到了的“已經失效的連接請求報文段”出現在本連接中。客戶端發送完最後一個確認報文後,在這個2MSL時間中,就可以使本連接持續的時間內所產生的所有報文段都從網絡中消失。這樣新的連接中不會出現舊連接的請求報文。

調優

在TIME_WAIT狀態無法真正釋放句柄資源,在此期間,Socket中使用的本地端口在默認情況下不能再被使用。該限制對於客戶端機器來說是無所謂的,但對於高併發服務器來說,會極大地限制有效連接的創建數量,稱爲性能瓶頸。所以建議將高併發服務器TIME_WAIT超時時間調小。RFC793中規定MSL爲2分鐘。但是在當前的高速網絡中,2分鐘的等待時間會造成資源的極大浪費,在高併發服務器上通常會使用更小的值。通過變更/etc/sysctl.conf文件來修改該默認值net.ipv4.tcp_fin_timout=30(建議小於30s)。修改完之後執行 /sbin/sysctl -p 讓參數生效。

爲什麼建立連接是三次握手,關閉連接確是四次揮手呢?

建立連接的時候, 服務器在LISTEN狀態下,收到建立連接請求的SYN報文後,把ACK和SYN放在一個報文裏發送給客戶端。 而關閉連接時,服務器收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,而自己也未必全部數據都發送給對方了,所以己方可以立即關閉,也可以發送一些數據給對方後,再發送FIN報文給對方來表示同意現在關閉連接,因此,己方ACK和FIN一般都會分開發送,從而導致多了一次。

滑動窗口

滑動窗口技術通過動態改變窗口大小來調節兩臺主機間的數據傳輸。每個TCP/IP主機支持全雙工數據傳輸,因此TCP有兩個滑動窗口:一個用於接收數據,另一個用於發送數據。TCP使用肯定確認技術,其確認號指的是下一個所期待的字節。 假定發送方以每一次三個數據包的方式發送數據,即窗口大小爲3。發送方發送序列號爲1、2、3的三個數據包,接收方成功接收數據包,用序列號4確認。發送方收到確認,繼續以窗口大小3發送數據。當接收方設備要求降低或者增大網絡流量時,可以對窗口大小進行減小或者增加,本例降低窗口大小爲2,每一次發送兩個數據包。當接收方設備要求窗口大小爲0,表明接收方已經接收了全部數據,或者接收方應用程序沒有時間讀取數據,要求暫停發送。發送方接收到攜帶窗口號爲0的確認,停止這一方向的數據傳輸。當鏈路變好了或者變差了這個窗口還會發生變化。

滑動窗口協議,是TCP使用的一種流量控制方法。該協議允許發送方在停止並等待確認前可以連續發送多個分組。由於發送方不必每發一個分組就停下來等待確認,因此該協議可以加速數據的傳輸。 只有在接收窗口向前滑動時(與此同時也發送了確認),發送窗口才有可能向前滑動。收發兩端的窗口按照以上規律不斷地向前滑動,因此這種協議又稱爲滑動窗口協議。

流量控制:端到端,接收端的應用層處理速度決定和網速無關,由接收端返回的rwnd控制

cwnd:發送端窗口( congestion window )

rwnd:接收端窗口(receiver window)

擁塞控制

 發送端主動控制cwnd,有慢啓動(從cwnd初始爲1開始啓動,指數啓動),擁塞避免(到達ssthresh後,爲了避免擁塞開始嘗試線性增長),快重傳(接收方每收到一個報文段都要回復一個當前最大連續位置的確認,發送方只要一連收到三個重複確認就知道接收方丟包了,快速重傳丟包的報文,並TCP馬上把擁塞窗口 cwnd 減小到1),快恢復(直接從ssthresh線性增長)。

如果網絡上的延時突然增加,那麼TCP對這個事作出的應對只有重傳數據,但是重傳會導致網絡的負擔更重,於是會導致更大的延遲以及更多的丟包,於是這個情況就會進入惡性循環被不斷地放大。對此TCP的設計理念是:當擁塞發生的時候,要做自我犧牲。就像交通阻塞一樣,每個車都應該把路讓出來,而不要再去搶路了。

慢啓動

只有在TCP連接建立和網絡出現超時時才使用。每經過一個傳輸輪次,擁塞窗口 cwnd 就加倍。一個傳輸輪次所經歷的時間其實就是往返時間RTT。慢開始的“慢”並不是指cwnd的增長速率慢,而是指在TCP開始發送報文段時先設置cwnd=1,使得發送方在開始時只發送一個報文段(目的是試探一下網絡的擁塞情況),然後再逐漸增大cwnd。

爲了防止擁塞窗口cwnd增長過大引起網絡擁塞,還需要設置一個慢開始門限ssthresh狀態變量。慢開始門限ssthresh的用法如下:

當 cwnd < ssthresh 時,使用上述的慢開始算法。

當 cwnd > ssthresh 時,停止使用慢開始算法而改用擁塞避免算法。

當 cwnd = ssthresh 時,既可使用慢開始算法,也可使用擁塞控制避免算法。

擁塞避免算法:讓擁塞窗口cwnd緩慢地增大,即每經過一個往返時間RTT就把發送方的擁塞窗口cwnd加1,而不是加倍。這樣擁塞窗口cwnd按線性規律緩慢增長,比慢開始算法的擁塞窗口增長速率緩慢得多。

無論在慢開始階段還是在擁塞避免階段,只要發送方判斷網絡出現擁塞(其根據就是沒有收到確認),就要把慢開始門限ssthresh設置爲出現擁塞時的發送方窗口值的一半(但不能小於2)。然後把擁塞窗口cwnd重新設置爲1,執行慢開始算法。這樣做的目的就是要迅速減少主機發送到網絡中的分組數,使得發生擁塞的路由器有足夠時間把隊列中積壓的分組處理完畢。

當TCP連接進行初始化時,把擁塞窗口cwnd置爲1。前面已說過,爲了便於理解,圖中的窗口單位不使用字節而使用報文段的個數。慢開始門限的初始值設置爲16個報文段,即 cwnd = 16 。

在執行慢開始算法時,擁塞窗口cwnd隨着傳輸輪次按指數規律增長。當擁塞窗口cwnd增長到慢開始門限值ssthresh時(即當cwnd=16時),就改爲執行擁塞控制算法,擁塞窗口按線性規律增長。

強調:“擁塞避免”並非指完全能夠避免了擁塞。利用以上的措施要完全避免網絡擁塞還是不可能的。“擁塞避免”是說在擁塞避免階段將擁塞窗口控制爲按線性規律增長,使網絡比較不容易出現擁塞。

如果發送方設置的超時計時器時限已到但還沒有收到確認,那麼很可能是網絡出現了擁塞,致使報文段在網絡中的某處被丟棄。這時,TCP馬上把擁塞窗口 cwnd 減小到1,並執行慢開始算法,同時把慢開始門限值ssthresh減半。這是不使用快重傳的情況。快重傳算法首先要求接收方每收到一個失序的報文段後就立即發出重複確認(爲的是使發送方及早知道有報文段沒有到達對方)而不要等到自己發送數據時才進行捎帶確認。

接收方收到了M1和M2後都分別發出了確認。現在假定接收方沒有收到M3但接着收到了M4。顯然,接收方不能確認M4,因爲M4是收到的失序報文段。根據可靠傳輸原理,接收方可以什麼都不做,也可以在適當時機發送一次對M2的確認。但按照快重傳算法的規定,接收方應及時發送對M2的重複確認,這樣做可以讓發送方及早知道報文段M3沒有到達接收方。發送方接着發送了M5和M6。接收方收到這兩個報文後,也還要再次發出對M2的重複確認。這樣,發送方共收到了接收方的四個對M2的確認,其中後三個都是重複確認。快重傳算法還規定,發送方只要一連收到三個重複確認就應當立即重傳對方尚未收到的報文段M3,而不必繼續等待M3設置的重傳計時器到期。由於發送方儘早重傳未被確認的報文段,因此採用快重傳後可以使整個網絡吞吐量提高約20%。

與快重傳配合使用的還有快恢復算法,其過程有以下兩個要點:

當發送方連續收到三個重複確認,就執行“乘法減小”算法,把慢啓動門限ssthresh減半。這是爲了預防網絡發生擁塞。請注意:接下去不執行慢開始算法。

由於發送方現在認爲網絡很可能沒有發生擁塞,因此與慢開始不同之處是現在不執行慢開始算法(即擁塞窗口cwnd現在不設置爲1),而是把cwnd值設置爲慢開始門限ssthresh減半後的數值,然後開始執行擁塞避免算法(“加法增大”),使擁塞窗口緩慢地線性增大。

上圖給出了快重傳和快恢復的示意圖,並標明瞭“TCP Reno版本”。區別:新的 TCP Reno 版本在快重傳之後採用快恢復算法而不是採用慢開始算法。

發送方窗口的上限值 = Min [ rwnd, cwnd ]

當rwnd < cwnd 時,是接收方的接收能力限制發送方窗口的最大值。

當cwnd < rwnd 時,則是網絡的擁塞限制發送方窗口的最大值。

差錯控制

TCP使用差錯控制來提供可靠性。差錯控制包括以下的一些機制:檢測和重傳受到損傷的報文段、重傳丟失的報文段、保存失序到達的報文段直至缺失的報文到期,以及檢測和丟棄重複的報文段。TCP通過三個簡單的工具來完成其差錯控制:檢驗和確認以及超時

TCP與UDP區別:

TCP:面向連接;可靠交付;全雙工通信;只支持一對一;

UDP:無連接不保證可靠;面向報文;無擁塞控制,適合多媒體傳輸;支持一對多,多對多等;首部開銷小

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