TCP的特點(diǎn)及其目的
為了通過數(shù)據(jù)包實(shí)現(xiàn)可靠性傳輸,需要考慮很多事情,例如數(shù)據(jù)的破壞、丟包、重復(fù)記憶分片順序混亂等問題。如不能解決這些問題,也就無從談起可靠傳輸。
TCP通過檢驗(yàn)和、序列號、確認(rèn)應(yīng)答、重發(fā)控制、連接管理以及窗口控制等機(jī)制實(shí)現(xiàn)可靠性傳輸。
通過序列號與確認(rèn)應(yīng)答提高可靠性
在TCP中,當(dāng)發(fā)送端的數(shù)據(jù)到達(dá)接收主機(jī)時,接收端主機(jī)會番號一個已收到消息的通知。這個消息叫做確認(rèn)應(yīng)答--ACK(Positive Acknowled-gement 意指已經(jīng)接收。)
TCP通過肯定的確認(rèn)應(yīng)答(ACK)實(shí)現(xiàn)可靠的數(shù)據(jù)傳輸。當(dāng)發(fā)送端將數(shù)據(jù)發(fā)生之后會等待對端的確認(rèn)應(yīng)答。如果有確認(rèn)應(yīng)答,說明數(shù)據(jù)已經(jīng)成功到達(dá)對端。反之,則數(shù)據(jù)丟失的可能性很大。
當(dāng)然,在一定時間內(nèi)沒有等到確認(rèn)應(yīng)答,發(fā)送端就可以認(rèn)為數(shù)據(jù)已經(jīng)丟失,并進(jìn)行重發(fā)。由此,即使產(chǎn)生了丟包,仍然能夠保證數(shù)據(jù)能夠到達(dá)對端,實(shí)現(xiàn)可靠傳輸。
未收到確認(rèn)應(yīng)答并不意味著數(shù)據(jù)一定丟失。也有可能是數(shù)據(jù)對方已經(jīng)收到,知識返回的確認(rèn)應(yīng)答在途中丟失。這種情況也會導(dǎo)致發(fā)送端因沒有收到確認(rèn)應(yīng)答,而認(rèn)為數(shù)據(jù)沒有到達(dá)目的地,從而進(jìn)行重新發(fā)送,如下圖:
此外,也有可能因?yàn)橐恍┢渌驅(qū)е麓_認(rèn)應(yīng)答延遲到達(dá),在源主機(jī)重發(fā)數(shù)據(jù)以后才到達(dá)的情況也屢見不鮮。此時,源發(fā)送主機(jī)只需重發(fā)數(shù)據(jù)即可,但是對目標(biāo)主機(jī),反復(fù)收到相同的數(shù)據(jù),確是一種"災(zāi)難"。而為了對上層應(yīng)用提供可靠的傳輸,必須得放棄重復(fù)的數(shù)據(jù)包。
為此,必須引入一種機(jī)制,它能夠識別出是否已經(jīng)接收數(shù)據(jù),又能夠判斷是否需要接收。
上述這些確認(rèn)應(yīng)答處理、重發(fā)控制以及重復(fù)控制等功能都可以通過序列號實(shí)現(xiàn)。
序列號是按順序給發(fā)送數(shù)據(jù)的每一個字節(jié)(8位字節(jié))都標(biāo)上號碼的編號。
接收到查詢接收數(shù)據(jù)TCP首部中的序列號和數(shù)據(jù)的長度,將自己下一步應(yīng)該接收的序號作為確認(rèn)應(yīng)答返送回去。
就這樣,通過序列號和確認(rèn)應(yīng)答號,TCP可以實(shí)現(xiàn)可靠傳輸。
重發(fā)超時
重發(fā)超時是指在重發(fā)數(shù)據(jù)之前,等待確認(rèn)應(yīng)答到來的那個特定時間間隔。如果超過了這個時間仍未收到確認(rèn)應(yīng)答,發(fā)送端將進(jìn)行數(shù)據(jù)重發(fā)。
在BSD的Unix以及Windows系統(tǒng)中,超時都以0.5秒為單位進(jìn)行控制,因此重發(fā)超時都是0.5秒的整數(shù)倍。不過,由于最初的數(shù)據(jù)包還不知從往返時間,所以其重發(fā)超時一般設(shè)置為6秒左右。
數(shù)據(jù)被重發(fā)之后如還是收不到確認(rèn)應(yīng)答,則進(jìn)行再次發(fā)送。此時,等待確認(rèn)應(yīng)答的時間將會以2倍、4倍的指數(shù)函數(shù)延長。
此外,數(shù)據(jù)也不會被無限、反復(fù)地重發(fā)。達(dá)到一定重發(fā)次數(shù)之后,如果人沒有任何確認(rèn)應(yīng)答返回,就會判斷為網(wǎng)絡(luò)或?qū)Χ酥鳈C(jī)發(fā)生了異常,強(qiáng)制關(guān)閉連接。并且通知應(yīng)用通信異常強(qiáng)行終止。
連接管理
TCP提供面向有連接的通信傳輸。面向有鏈接是指在數(shù)據(jù)通信開始之前先做好通信兩端之間的準(zhǔn)備工作。
一個連接的建立與斷開,正常過程至少需要來回發(fā)送7個包才能完成。
三次握手
TCP是面向連接的,無論哪一方向另一方發(fā)送數(shù)據(jù)之前,都必須先在雙方之間建立一條連接。在TCP/IP協(xié)議中,TCP協(xié)議提供可靠的連接服務(wù),連接是通過三次握手進(jìn)行初始化的。三次握手的目的是同步連接雙方的序列號和確認(rèn)號并交換 TCP窗口大小信息。這就是面試中經(jīng)常會被問到的TCP三次握手。只是了解TCP三次握手的概念,對你獲得一份工作是沒有任何幫助的,你需要去了解TCP三次握手中的一些細(xì)節(jié)。先來看圖說話。
第一次握手:建立連接。客戶端發(fā)送連接請求報(bào)文段,將SYN位置為1,Sequence Number為x;然后,客戶端進(jìn)入SYN_SEND狀態(tài),等待服務(wù)器的確認(rèn);
第二次握手:服務(wù)器收到SYN報(bào)文段。服務(wù)器收到客戶端的SYN報(bào)文段,需要對這個SYN報(bào)文段進(jìn)行確認(rèn),設(shè)置Acknowledgment Number為x+1(Sequence Number+1);同時,自己自己還要發(fā)送SYN請求信息,將SYN位置為1,Sequence Number為y;服務(wù)器端將上述所有信息放到一個報(bào)文段(即SYN+ACK報(bào)文段)中,一并發(fā)送給客戶端,此時服務(wù)器進(jìn)入SYN_RECV狀態(tài);
第三次握手:客戶端收到服務(wù)器的SYN+ACK報(bào)文段。然后將Acknowledgment Number設(shè)置為y+1,向服務(wù)器發(fā)送ACK報(bào)文段,這個報(bào)文段發(fā)送完畢以后,客戶端和服務(wù)器端都進(jìn)入ESTABLISHED狀態(tài),完成TCP三次握手。
完成了三次握手,客戶端和服務(wù)器端就可以開始傳送數(shù)據(jù)。以上就是TCP三次握手的總體介紹。
為什么要三次握手
既然總結(jié)了TCP的三次握手,那為什么非要三次呢?怎么覺得兩次就可以完成了。那TCP為什么非要進(jìn)行三次連接呢?在謝希仁的《計(jì)算機(jī)網(wǎng)絡(luò)》中是這樣說的:
為了防止已失效的連接請求報(bào)文段突然又傳送到了服務(wù)端,因而產(chǎn)生錯誤。
在書中同時舉了一個例子,如下:
已失效的連接請求報(bào)文段”的產(chǎn)生在這樣一種情況下:client發(fā)出的第一個連接請求報(bào)文段并沒有丟失,而是在某個網(wǎng)絡(luò)結(jié)點(diǎn)長時間的滯留了,以致延誤到連接釋放以后的某個時間才到達(dá)server。本來這是一個早已失效的報(bào)文段。但server收到此失效的連接請求報(bào)文段后,就誤認(rèn)為是client再次發(fā)出的一個新的連接請求。于是就向client發(fā)出確認(rèn)報(bào)文段,同意建立連接。假設(shè)不采用“三次握手”,那么只要server發(fā)出確認(rèn),新的連接就建立了。由于現(xiàn)在client并沒有發(fā)出建立連接的請求,因此不會理睬server的確認(rèn),也不會向server發(fā)送數(shù)據(jù)。但server卻以為新的運(yùn)輸連接已經(jīng)建立,并一直等待client發(fā)來數(shù)據(jù)。這樣,server的很多資源就白白浪費(fèi)掉了。采用“三次握手”的辦法可以防止上述現(xiàn)象發(fā)生。例如剛才那種情況,client不會向server的確認(rèn)發(fā)出確認(rèn)。server由于收不到確認(rèn),就知道client并沒有要求建立連接。
這就很明白了,防止了服務(wù)器端的一直等待而浪費(fèi)資源。
四次揮手
當(dāng)客戶端和服務(wù)器通過三次握手建立了TCP連接以后,當(dāng)數(shù)據(jù)傳送完畢,肯定是要斷開TCP連接的啊。那對于TCP的斷開連接,這里就有了神秘的“四次揮手”。
在socket編程中,任何一方執(zhí)行close()操作即可產(chǎn)生揮手操作。
第一次揮手:主機(jī)1(可以使客戶端,也可以是服務(wù)器端),設(shè)置Sequence Number和Acknowledgment Number,向主機(jī)2發(fā)送一個FIN報(bào)文段;此時,主機(jī)1進(jìn)入FIN_WAIT_1狀態(tài);這表示主機(jī)1沒有數(shù)據(jù)要發(fā)送給主機(jī)2了;
第二次揮手:主機(jī)2收到了主機(jī)1發(fā)送的FIN報(bào)文段,向主機(jī)1回一個ACK報(bào)文段,Acknowledgment Number為Sequence Number加1;主機(jī)1進(jìn)入FIN_WAIT_2狀態(tài);主機(jī)2告訴主機(jī)1,我“同意”你的關(guān)閉請求;
第三次揮手:主機(jī)2向主機(jī)1發(fā)送FIN報(bào)文段,請求關(guān)閉連接,同時主機(jī)2進(jìn)入LAST_ACK狀態(tài);
第四次揮手:主機(jī)1收到主機(jī)2發(fā)送的FIN報(bào)文段,向主機(jī)2發(fā)送ACK報(bào)文段,然后主機(jī)1進(jìn)入TIME_WAIT狀態(tài);主機(jī)2收到主機(jī)1的ACK報(bào)文段以后,就關(guān)閉連接;此時,主機(jī)1等待2MSL后依然沒有收到回復(fù),則證明Server端已正常關(guān)閉,那好,主機(jī)1也可以關(guān)閉連接了。
至此,TCP的四次揮手就這么愉快的完成了。當(dāng)你看到這里,你的腦子里會有很多的疑問,很多的不懂,感覺很凌亂;沒事,我們繼續(xù)總結(jié)。
為什么要四次揮手
那四次揮手又是為何呢?TCP協(xié)議是一種面向連接的、可靠的、基于字節(jié)流的運(yùn)輸層通信協(xié)議。TCP是全雙工模式,這就意味著,當(dāng)主機(jī)1發(fā)出FIN報(bào)文段時,只是表示主機(jī)1已經(jīng)沒有數(shù)據(jù)要發(fā)送了,主機(jī)1告訴主機(jī)2,它的數(shù)據(jù)已經(jīng)全部發(fā)送完畢了;但是,這個時候主機(jī)1還是可以接受來自主機(jī)2的數(shù)據(jù);當(dāng)主機(jī)2返回ACK報(bào)文段時,表示它已經(jīng)知道主機(jī)1沒有數(shù)據(jù)發(fā)送了,但是主機(jī)2還是可以發(fā)送數(shù)據(jù)到主機(jī)1的;當(dāng)主機(jī)2也發(fā)送了FIN報(bào)文段時,這個時候就表示主機(jī)2也沒有數(shù)據(jù)要發(fā)送了,就會告訴主機(jī)1,我也沒有數(shù)據(jù)要發(fā)送了,之后彼此就會愉快的中斷這次TCP連接。
如果要正確的理解四次揮手的原理,就需要了解四次揮手過程中的狀態(tài)變化。
FIN_WAIT_1: 這個狀態(tài)要好好解釋一下,其實(shí)FIN_WAIT_1和FIN_WAIT_2狀態(tài)的真正含義都是表示等待對方的FIN報(bào)文。而這兩種狀態(tài)的區(qū)別是:FIN_WAIT_1狀態(tài)實(shí)際上是當(dāng)SOCKET在ESTABLISHED狀態(tài)時,它想主動關(guān)閉連接,向?qū)Ψ桨l(fā)送了FIN報(bào)文,此時該SOCKET即進(jìn)入到FIN_WAIT_1狀態(tài)。而當(dāng)對方回應(yīng)ACK報(bào)文后,則進(jìn)入到FIN_WAIT_2狀態(tài),當(dāng)然在實(shí)際的正常情況下,無論對方何種情況下,都應(yīng)該馬上回應(yīng)ACK報(bào)文,所以FIN_WAIT_1狀態(tài)一般是比較難見到的,而FIN_WAIT_2狀態(tài)還有時常常可以用netstat看到。(主動方)
FIN_WAIT_2:上面已經(jīng)詳細(xì)解釋了這種狀態(tài),實(shí)際上FIN_WAIT_2狀態(tài)下的SOCKET,表示半連接,也即有一方要求close連接,但另外還告訴對方,我暫時還有點(diǎn)數(shù)據(jù)需要傳送給你(ACK信息),稍后再關(guān)閉連接。(主動方)
CLOSE_WAIT:這種狀態(tài)的含義其實(shí)是表示在等待關(guān)閉。怎么理解呢?當(dāng)對方close一個SOCKET后發(fā)送FIN報(bào)文給自己,你系統(tǒng)毫無疑問地會回應(yīng)一個ACK報(bào)文給對方,此時則進(jìn)入到CLOSE_WAIT狀態(tài)。接下來呢,實(shí)際上你真正需要考慮的事情是察看你是否還有數(shù)據(jù)發(fā)送給對方,如果沒有的話,那么你也就可以 close這個SOCKET,發(fā)送FIN報(bào)文給對方,也即關(guān)閉連接。所以你在CLOSE_WAIT狀態(tài)下,需要完成的事情是等待你去關(guān)閉連接。(被動方)
LAST_ACK: 這個狀態(tài)還是比較容易好理解的,它是被動關(guān)閉一方在發(fā)送FIN報(bào)文后,最后等待對方的ACK報(bào)文。當(dāng)收到ACK報(bào)文后,也即可以進(jìn)入到CLOSED可用狀態(tài)了。(被動方)
TIME_WAIT: 表示收到了對方的FIN報(bào)文,并發(fā)送出了ACK報(bào)文,就等2MSL后即可回到CLOSED可用狀態(tài)了。如果FINWAIT1狀態(tài)下,收到了對方同時帶FIN標(biāo)志和ACK標(biāo)志的報(bào)文時,可以直接進(jìn)入到TIME_WAIT狀態(tài),而無須經(jīng)過FIN_WAIT_2狀態(tài)。(主動方)
CLOSED: 表示連接中斷。
TCP以段為單位發(fā)送數(shù)據(jù)
TCP在傳送大量數(shù)據(jù)時,是以MSS(Maximum Segment Size 最大消息長度)的大小將數(shù)據(jù)進(jìn)行分割發(fā)送。進(jìn)行重發(fā)是也是以MSS為單位。
MSS是在三次握手的時候,在兩端主機(jī)之間被計(jì)算得出劇好兩端的主機(jī)在發(fā)出建立連接的請求是,會在TCP首部中寫入MSS選項(xiàng),告訴對方自己的接口能夠適的MSS的大小。然后會在兩者之間選擇一個較小的值投入使用。
利用窗口控制提高速度
TCP以1個段為單位,每發(fā)一個段進(jìn)行一次確認(rèn)應(yīng)答的處理。
如圖:
為了解決通信性能的問題,TCP引入了窗口這個概念。
確認(rèn)應(yīng)答不再以每個分段,而是以更大的單位進(jìn)行確認(rèn)時,轉(zhuǎn)發(fā)時間將會被大幅度的縮短。也就是說,發(fā)送端主機(jī),在發(fā)送了一個段以后不必要一直等待確認(rèn)應(yīng)答,而是繼續(xù)發(fā)送。
窗口大小就是指無需等待確認(rèn)應(yīng)答而可以繼續(xù)發(fā)送數(shù)據(jù)的最大值。上圖中的窗口大小為4個段。
這個機(jī)制實(shí)現(xiàn)了使用大量的緩沖區(qū),通過對多個段同時進(jìn)行確認(rèn)應(yīng)答的功能。
如上圖中,發(fā)送數(shù)據(jù)中高亮圈起的部分正是前面所提到的窗口。
在收到確認(rèn)應(yīng)答后,將窗口滑動到確認(rèn)應(yīng)答中的序列號的位置。這樣可以順序地將多個段同時發(fā)送提高通信性能。這種機(jī)制也被稱為滑動窗口控制。
窗口控制與重發(fā)控制
在未使用窗口控制時,沒有收到確認(rèn)應(yīng)答的數(shù)據(jù)都會被重發(fā)。而是用了窗口控制,某些確認(rèn)應(yīng)答即使丟失也無需重發(fā)
其次,我們來考慮一下某個報(bào)文段丟失的情況。接收主機(jī)如果收到一個自己應(yīng)該接收的序號以外的數(shù)據(jù)時,會針對當(dāng)前位置收到的數(shù)據(jù)返回確認(rèn)應(yīng)答。
當(dāng)某一報(bào)文段丟失后,發(fā)送端會一直收到序號為1001的確認(rèn)應(yīng)答,這個確認(rèn)應(yīng)答提醒發(fā)送端,"我想接收的是從1001開始的數(shù)據(jù)"。
因此,當(dāng)窗口比較大時,又出現(xiàn)報(bào)文段丟失是,同一序號的確認(rèn)應(yīng)答會不斷重復(fù)的返回。
當(dāng)發(fā)送端主機(jī)如果連續(xù)3次收到同一個確認(rèn)應(yīng)答,就會將其所對應(yīng)的數(shù)據(jù)進(jìn)行重發(fā)。
這種機(jī)制,比之前提到超時管理更搞笑,因此也被稱作高速重發(fā)控制。
流控制
流控制是TCP提供的一種可以讓發(fā)送端根據(jù)接受端的實(shí)際接收能力控制發(fā)送的數(shù)據(jù)量的機(jī)制。
具體操作是,接收端主機(jī)箱發(fā)送端主機(jī)通知自己可以接收數(shù)據(jù)的大小,于是發(fā)送端會發(fā)送不超過這個限度的數(shù)據(jù)。該大小限度就被稱作窗口大小。
TCP首部中,專門有一個字段用來通知窗口大小。
接收主機(jī)將自己可以接收的緩存區(qū)大小放入這個字段中通知給發(fā)送端。這個字段的值越大,說明網(wǎng)絡(luò)的吞吐量越高。
不過,當(dāng)接收端的緩沖區(qū)面臨數(shù)據(jù)溢出時,窗口大小的值也會隨之唄設(shè)置為一個更小的值通知給發(fā)送端,從而控制數(shù)據(jù)發(fā)送量。
也就是說,發(fā)送端主機(jī)會根據(jù)接收端主機(jī)的指示,對發(fā)送數(shù)據(jù)的量進(jìn)行控制。這也就形成了一個完整的TCP流控制(流量控制)
如上圖,當(dāng)接收端從3001號開始的數(shù)據(jù)段后其緩沖區(qū)即滿,不得不暫時停止接收數(shù)據(jù)。之后,在收到發(fā)送窗口更新通知后通信才得以繼續(xù)進(jìn)行。
擁塞控制
有了窗口控制,首發(fā)主機(jī)之間可以不再以一個數(shù)據(jù)段為單位發(fā)送確認(rèn)應(yīng)答,也能夠連續(xù)發(fā)送大量數(shù)據(jù)包。但是如果在通信剛開始時就發(fā)送大量數(shù)據(jù),也有可能會引發(fā)其他問題。
TCP為了防止該問題的出現(xiàn),在通信一開始時就會通過一個叫做慢啟動的算法得出的數(shù)值,對發(fā)送數(shù)據(jù)量進(jìn)行控制。
首先,為了在發(fā)送端調(diào)節(jié)所要發(fā)送數(shù)據(jù)的量,定義了一個叫做"擁堵窗口"的概念。于是在慢啟動的時候,將這個擁堵窗口的大小設(shè)置為1個數(shù)據(jù)段(1MSS)發(fā)送數(shù)據(jù),之后每收到一次確認(rèn)應(yīng)答(ACK),擁堵窗口的值就加1。在發(fā)送數(shù)據(jù)包時,將擁堵窗口的大小與接收端主機(jī)通知的窗口大小做比較,然后按照它們當(dāng)中較小那個值,發(fā)送比其還要小的數(shù)據(jù)量。
如果重發(fā)采用超時機(jī)制,那么擁塞窗口的初始值可以設(shè)置為1以后再進(jìn)行慢啟動修正。有了上述這些機(jī)制,就可以有限的減少通信開始時連續(xù)發(fā)包導(dǎo)致的網(wǎng)絡(luò)擁堵,還可以避免網(wǎng)絡(luò)擁塞情況的發(fā)生。
不過,隨著包的每次往返,擁塞窗口也會以1、2、4等指數(shù)函數(shù)的增長,擁堵狀況激增甚至導(dǎo)致網(wǎng)絡(luò)擁塞的發(fā)生。為了防止這些,引入了慢啟動閥值的概念。只要擁塞窗口的值超出這個閥值,在每收到一次確認(rèn)應(yīng)答時,只允許以下面這種比例方法擁塞窗口:
當(dāng)TCP通信開始以后,網(wǎng)絡(luò)吞吐量會逐漸上升,但是隨著網(wǎng)絡(luò)擁堵的發(fā)生吞吐量也會急劇下降。于是會再次進(jìn)入吞吐量慢慢上升的過程。因此所謂TCP的吞吐量的特點(diǎn)就好像是在逐步占領(lǐng)網(wǎng)絡(luò)帶寬的感覺。
提高網(wǎng)絡(luò)利用率的規(guī)范
- Nagle算法
- 延遲確認(rèn)應(yīng)答
- 捎帶應(yīng)答