爲了經過數據包實現可靠性傳輸,須要考慮不少事情,例如數據的破壞、丟包、重複記憶分片順序混亂等問題。如不能解決這些問題,也就無從談起可靠傳輸。
TCP經過檢驗和、序列號、確認應答、重發控制、鏈接管理以及窗口控制等機制實現可靠性傳輸。面試
在TCP中,當發送端的數據到達接收主機時,接收端主機會番號一個已收到消息的通知。這個消息叫作確認應答--ACK(Positive Acknowled-gement 意指已經接收。)算法
TCP經過確定的確認應答(ACK)實現可靠的數據傳輸。當發送端將數據發生以後會等待對端的確認應答。若是有確認應答,說明數據已經成功到達對端。反之,則數據丟失的可能性很大。編程
固然,在必定時間內沒有等到確認應答,發送端就能夠認爲數據已經丟失,並進行重發。由此,即便產生了丟包,仍然可以保證數據可以到達對端,實現可靠傳輸。緩存
未收到確認應答並不意味着數據必定丟失。也有多是數據對方已經收到,知識返回的確認應答在途中丟失。這種狀況也會致使發送端因沒有收到確認應答,而認爲數據沒有到達目的地,從而進行從新發送,以下圖:服務器
此外,也有可能由於一些其餘緣由致使確認應答延遲到達,在源主機重發數據之後纔到達的狀況也家常便飯。此時,源發送主機只需重發數據便可,可是對目標主機,反覆收到相同的數據,確是一種"災難"。而爲了對上層應用提供可靠的傳輸,必須得放棄重複的數據包。網絡
爲此,必須引入一種機制,它可以識別出是否已經接收數據,又可以判斷是否須要接收。
上述這些確認應答處理、重發控制以及重複控制等功能均可以經過序列號實現。
序列號是按順序給發送數據的每個字節(8位字節)都標上號碼的編號。
接收到查詢接收數據TCP首部中的序列號和數據的長度,將本身下一步應該接收的序號做爲確認應答返送回去。
就這樣,經過序列號和確認應答號,TCP能夠實現可靠傳輸。併發
重發超時是指在重發數據以前,等待確認應答到來的那個特定時間間隔。若是超過了這個時間仍未收到確認應答,發送端將進行數據重發。socket
在BSD的Unix以及Windows系統中,超時都以0.5秒爲單位進行控制,所以重發超時都是0.5秒的整數倍。不過,因爲最初的數據包還不知從往返時間,因此其重發超時通常設置爲6秒左右。tcp
數據被重發以後如仍是收不到確認應答,則進行再次發送。此時,等待確認應答的時間將會以2倍、4倍的指數函數延長。函數
此外,數據也不會被無限、反覆地重發。達到必定重發次數以後,若是人沒有任何確認應答返回,就會判斷爲網絡或對端主機發生了異常,強制關閉鏈接。而且通知應用通訊異常強行終止。
TCP提供面向有鏈接的通訊傳輸。面向有連接是指在數據通訊開始以前先作好通訊兩端之間的準備工做。
一個鏈接的創建與斷開,正常過程至少須要來回發送7個包才能完成。
TCP是面向鏈接的,不管哪一方向另外一方發送數據以前,都必須先在雙方之間創建一條鏈接。在TCP/IP協議中,TCP協議提供可靠的鏈接服務,鏈接是經過三次握手進行初始化的。三次握手的目的是同步鏈接雙方的序列號和確認號並交換 TCP窗口大小信息。這就是面試中常常會被問到的TCP三次握手。只是瞭解TCP三次握手的概念,對你得到一份工做是沒有任何幫助的,你須要去了解TCP三次握手中的一些細節。先來看圖說話。
第一次握手:創建鏈接。客戶端發送鏈接請求報文段,將SYN位置爲1,Sequence Number爲x;而後,客戶端進入SYN_SEND狀態,等待服務器的確認;
第二次握手:服務器收到SYN報文段。服務器收到客戶端的SYN報文段,須要對這個SYN報文段進行確認,設置Acknowledgment Number爲x+1(Sequence Number+1);同時,本身本身還要發送SYN請求信息,將SYN位置爲1,Sequence Number爲y;服務器端將上述全部信息放到一個報文段(即SYN+ACK報文段)中,一併發送給客戶端,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK報文段。而後將Acknowledgment Number設置爲y+1,向服務器發送ACK報文段,這個報文段發送完畢之後,客戶端和服務器端都進入ESTABLISHED狀態,完成TCP三次握手。
完成了三次握手,客戶端和服務器端就能夠開始傳送數據。以上就是TCP三次握手的整體介紹。
既然總結了TCP的三次握手,那爲何非要三次呢?怎麼以爲兩次就能夠完成了。那TCP爲何非要進行三次鏈接呢?在謝希仁的《計算機網絡》中是這樣說的:
爲了防止已失效的鏈接請求報文段忽然又傳送到了服務端,於是產生錯誤。
在書中同時舉了一個例子,以下:
已失效的鏈接請求報文段」的產生在這樣一種狀況下:client發出的第一個鏈接請求報文段並無丟失,而是在某個網絡結點長時間的滯留了,以至延誤到鏈接釋放之後的某個時間纔到達server。原本這是一個早已失效的報文段。但server收到此失效的鏈接請求報文段後,就誤認爲是client再次發出的一個新的鏈接請求。因而就向client發出確認報文段,贊成創建鏈接。假設不採用「三次握手」,那麼只要server發出確認,新的鏈接就創建了。因爲如今client並無發出創建鏈接的請求,所以不會理睬server的確認,也不會向server發送數據。但server卻覺得新的運輸鏈接已經創建,並一直等待client發來數據。這樣,server的不少資源就白白浪費掉了。採用「三次握手」的辦法能夠防止上述現象發生。例如剛纔那種狀況,client不會向server的確認發出確認。server因爲收不到確認,就知道client並無要求創建鏈接。
這就很明白了,防止了服務器端的一直等待而浪費資源。
當客戶端和服務器經過三次握手創建了TCP鏈接之後,當數據傳送完畢,確定是要斷開TCP鏈接的啊。那對於TCP的斷開鏈接,這裏就有了神祕的「四次揮手」。
在socket編程中,任何一方執行close()操做便可產生揮手操做。
第一次揮手:主機1(可使客戶端,也能夠是服務器端),設置Sequence Number和Acknowledgment Number,向主機2發送一個FIN報文段;此時,主機1進入FIN_WAIT_1狀態;這表示主機1沒有數據要發送給主機2了;
第二次揮手:主機2收到了主機1發送的FIN報文段,向主機1回一個ACK報文段,Acknowledgment Number爲Sequence Number加1;主機1進入FIN_WAIT_2狀態;主機2告訴主機1,我「贊成」你的關閉請求;
第三次揮手:主機2向主機1發送FIN報文段,請求關閉鏈接,同時主機2進入LAST_ACK狀態;
第四次揮手:主機1收到主機2發送的FIN報文段,向主機2發送ACK報文段,而後主機1進入TIME_WAIT狀態;主機2收到主機1的ACK報文段之後,就關閉鏈接;此時,主機1等待2MSL後依然沒有收到回覆,則證實Server端已正常關閉,那好,主機1也能夠關閉鏈接了。
至此,TCP的四次揮手就這麼愉快的完成了。當你看到這裏,你的腦子裏會有不少的疑問,不少的不懂,感受很凌亂;沒事,咱們繼續總結。
那四次揮手又是爲什麼呢?TCP協議是一種面向鏈接的、可靠的、基於字節流的運輸層通訊協議。TCP是全雙工模式,這就意味着,當主機1發出FIN報文段時,只是表示主機1已經沒有數據要發送了,主機1告訴主機2,它的數據已經所有發送完畢了;可是,這個時候主機1仍是能夠接受來自主機2的數據;當主機2返回ACK報文段時,表示它已經知道主機1沒有數據發送了,可是主機2仍是能夠發送數據到主機1的;當主機2也發送了FIN報文段時,這個時候就表示主機2也沒有數據要發送了,就會告訴主機1,我也沒有數據要發送了,以後彼此就會愉快的中斷此次TCP鏈接。
若是要正確的理解四次揮手的原理,就須要瞭解四次揮手過程當中的狀態變化。
FIN_WAIT_1: 這個狀態要好好解釋一下,其實FIN_WAIT_1和FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報文。而這兩種狀態的區別是:FIN_WAIT_1狀態其實是當SOCKET在ESTABLISHED狀態時,它想主動關閉鏈接,向對方發送了FIN報文,此時該SOCKET即進入到FIN_WAIT_1狀態。而當對方迴應ACK報文後,則進入到FIN_WAIT_2狀態,固然在實際的正常狀況下,不管對方何種狀況下,都應該立刻迴應ACK報文,因此FIN_WAIT_1狀態通常是比較難見到的,而FIN_WAIT_2狀態還有時經常能夠用netstat看到。(主動方)
FIN_WAIT_2:上面已經詳細解釋了這種狀態,實際上FIN_WAIT_2狀態下的SOCKET,表示半鏈接,也即有一方要求close鏈接,但另外還告訴對方,我暫時還有點數據須要傳送給你(ACK信息),稍後再關閉鏈接。(主動方)
CLOSE_WAIT:這種狀態的含義實際上是表示在等待關閉。怎麼理解呢?當對方close一個SOCKET後發送FIN報文給本身,你係統毫無疑問地會迴應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。接下來呢,實際上你真正須要考慮的事情是察看你是否還有數據發送給對方,若是沒有的話,那麼你也就能夠 close這個SOCKET,發送FIN報文給對方,也即關閉鏈接。因此你在CLOSE_WAIT狀態下,須要完成的事情是等待你去關閉鏈接。(被動方)
LAST_ACK: 這個狀態仍是比較容易好理解的,它是被動關閉一方在發送FIN報文後,最後等待對方的ACK報文。當收到ACK報文後,也便可以進入到CLOSED可用狀態了。(被動方)
TIME_WAIT: 表示收到了對方的FIN報文,併發送出了ACK報文,就等2MSL後便可回到CLOSED可用狀態了。若是FINWAIT1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,能夠直接進入到TIME_WAIT狀態,而無須通過FIN_WAIT_2狀態。(主動方)
CLOSED: 表示鏈接中斷。
TCP在傳送大量數據時,是以MSS(Maximum Segment Size 最大消息長度)的大小將數據進行分割發送。進行重發是也是以MSS爲單位。
MSS是在三次握手的時候,在兩端主機之間被計算得出劇好兩端的主機在發出創建鏈接的請求是,會在TCP首部中寫入MSS選項,告訴對方本身的接口可以適的MSS的大小。而後會在二者之間選擇一個較小的值投入使用。
TCP以1個段爲單位,每發一個段進行一次確認應答的處理。
如圖:
爲了解決通訊性能的問題,TCP引入了窗口這個概念。
確認應答再也不以每一個分段,而是以更大的單位進行確認時,轉發時間將會被大幅度的縮短。也就是說,發送端主機,在發送了一個段之後沒必要要一直等待確認應答,而是繼續發送。
窗口大小就是指無需等待確認應答而能夠繼續發送數據的最大值。上圖中的窗口大小爲4個段。
這個機制實現了使用大量的緩衝區,經過對多個段同時進行確認應答的功能。
如上圖中,發送數據中高亮圈起的部分正是前面所提到的窗口。
在收到確認應答後,將窗口滑動到確認應答中的序列號的位置。這樣能夠順序地將多個段同時發送提升通訊性能。這種機制也被稱爲滑動窗口控制。
在未使用窗口控制時,沒有收到確認應答的數據都會被重發。而是用了窗口控制,某些確認應答即便丟失也無需重發
其次,咱們來考慮一下某個報文段丟失的狀況。接收主機若是收到一個本身應該接收的序號之外的數據時,會針對當前位置收到的數據返回確認應答。
當某一報文段丟失後,發送端會一直收到序號爲1001的確認應答,這個確認應答提醒發送端,"我想接收的是從1001開始的數據"。
所以,當窗口比較大時,又出現報文段丟失是,同一序號的確認應答會不斷重複的返回。
當發送端主機若是連續3次收到同一個確認應答,就會將其所對應的數據進行重發。
這種機制,比以前提到超時管理更搞笑,所以也被稱做高速重發控制。
流控制是TCP提供的一種可讓發送端根據接受端的實際接收能力控制發送的數據量的機制。
具體操做是,接收端主機箱發送端主機通知本身能夠接收數據的大小,因而發送端會發送不超過這個限度的數據。該大小限度就被稱做窗口大小。
TCP首部中,專門有一個字段用來通知窗口大小。
接收主機將本身能夠接收的緩存區大小放入這個字段中通知給發送端。這個字段的值越大,說明網絡的吞吐量越高。
不過,當接收端的緩衝區面臨數據溢出時,窗口大小的值也會隨之唄設置爲一個更小的值通知給發送端,從而控制數據發送量。
也就是說,發送端主機會根據接收端主機的指示,對發送數據的量進行控制。這也就造成了一個完整的TCP流控制(流量控制)
如上圖,當接收端從3001號開始的數據段後其緩衝區即滿,不得不暫時中止接收數據。以後,在收到發送窗口更新通知後通訊才得以繼續進行。
有了窗口控制,首發主機之間能夠再也不以一個數據段爲單位發送確認應答,也可以連續發送大量數據包。可是若是在通訊剛開始時就發送大量數據,也有可能會引起其餘問題。
TCP爲了防止該問題的出現,在通訊一開始時就會經過一個叫作慢啓動的算法得出的數值,對發送數據量進行控制。
首先,爲了在發送端調節所要發送數據的量,定義了一個叫作"擁堵窗口"的概念。因而在慢啓動的時候,將這個擁堵窗口的大小設置爲1個數據段(1MSS)發送數據,以後每收到一次確認應答(ACK),擁堵窗口的值就加1。在發送數據包時,將擁堵窗口的大小與接收端主機通知的窗口大小作比較,而後按照它們當中較小那個值,發送比其還要小的數據量。
若是重發採用超時機制,那麼擁塞窗口的初始值能夠設置爲1之後再進行慢啓動修正。有了上述這些機制,就能夠有限的減小通訊開始時連續發包致使的網絡擁堵,還能夠避免網絡擁塞狀況的發生。
不過,隨着包的每次往返,擁塞窗口也會以一、二、4等指數函數的增加,擁堵情況激增甚至致使網絡擁塞的發生。爲了防止這些,引入了慢啓動閥值的概念。只要擁塞窗口的值超出這個閥值,在每收到一次確認應答時,只容許如下面這種比例方法擁塞窗口:
當TCP通訊開始之後,網絡吞吐量會逐漸上升,可是隨着網絡擁堵的發生吞吐量也會急劇降低。因而會再次進入吞吐量慢慢上升的過程。所以所謂TCP的吞吐量的特色就好像是在逐步佔領網絡帶寬的感受。