TCP協(xié)議
面向字節(jié)流的點對點全雙工通信方式。TCP傳輸?shù)臄?shù)據(jù)單元叫報文段,TCP報文段 = TCP首部 + TCP數(shù)據(jù)部分。一個完整的業(yè)務(wù)可能會被TCP拆分成多個包進行發(fā)送,也有可能把多個小的包封裝成一個大的數(shù)據(jù)包發(fā)送,這個是TCP的拆包和粘包。應(yīng)用程序?qū)懭霐?shù)據(jù)的字節(jié)大小可能大于套接字發(fā)送緩沖區(qū)的大小,此時就需要進行MSS大小的TCP分段( MSS=TCP報文段長度-TCP首部長度)。

幾個重要字段的含義:
序列號:TCP是面向字節(jié)流的,故每一個字節(jié)都有一個編號,幀的序號就是本幀第一個字節(jié)的編號。
確認號:接收方希望收到下一幀的第一個字節(jié)的序號。
首部長度:占四位,以4B為計算單位。
確認位ACK:ACK = 1時,確認號字段才有效,TCP規(guī)定建立連接后所有的報文段都必須把ACK置1。
同步位SYN:SYN = 1表示這是一個連接請求或連接接收報文。
終止位FIN:用來釋放一個連接,F(xiàn)IN = 1表示此報文段發(fā)送方的數(shù)據(jù)已經(jīng)發(fā)送完了,請求釋放傳輸連接。
窗口字段:允許對方發(fā)送的數(shù)據(jù)量。
校驗和:首部和數(shù)據(jù)兩部分的校驗,計算時需要加上12B的偽首部。
選項字段:目前只規(guī)定了一種選項即MSS(最大報文段長度)。
確認應(yīng)答機制
每一個ACK都帶有對應(yīng)的確認序列號,意思是告訴發(fā)送者,我已經(jīng)收到了哪些數(shù)據(jù);下一次你從哪里開始發(fā)。TCP使用累計確認,收到某一個確認序號,即代表前面的幀全部接受對。
重傳機制
發(fā)送過程中會發(fā)生丟包?的現(xiàn)象, 而 丟包可能會丟普通報文, 也可能會丟 ACK ,兩種情況下,發(fā)送方都不知道數(shù)據(jù)對方是否收到。TCP具有亂序重組的功能,所以失序時,接收方對失序的報文段也會進行暫存。TCP重傳方式:
超時重傳機制:在一段時間內(nèi) 沒收到應(yīng)答 ,發(fā)送方就會再發(fā)送一次數(shù)據(jù)。對于超時重傳機制,多久沒收到應(yīng)答進行重新發(fā)送的時間間隔, 會越來越長. 如 : 第一次發(fā)送沒收到, 間隔1秒再發(fā)送 , 第二次發(fā)送又沒收到 ,就會間隔 2 秒再發(fā)送, 以此類推. 也不會一直重新發(fā)送, 嘗試幾次后, 如果仍然不成功,就會斷開連接,重新嘗試連接. 重連也連不上,就放棄了。
冗余ACK:再次確認某個報文段的ACK。超時重傳的缺點是周期太長,可用冗余ACK進行快速重傳。例如:如果發(fā)送方發(fā)出了1,2,3,4,5份數(shù)據(jù),第一份先到送了,于是就ack回2,結(jié)果2因為某些原因沒收到,3到達了,于是還是ack回2,后面的4和5都到了,但是還是ack回2,因為2還是沒有收到,于是發(fā)送端收到了三個ack=2的確認,知道2還沒有到,于是就馬上重轉(zhuǎn)2。然后,接收端收到了2,此時因為3,4,5都收到了,于是ack回6。于是引入SACK選擇確認選項,若收到的報文段無差錯,只是未按序號,中間還缺少一些序號的數(shù)據(jù);通過選擇確認?,可以只傳送缺少的數(shù)據(jù)而不重傳已經(jīng)正確到達接受方的數(shù)據(jù)。
TCP三次握手連接
客戶端連接請求報文段:只有頭部數(shù)據(jù),序號為x,雖不含任何數(shù)據(jù),但要消耗掉一個序號:SYN = 1,seq = x。
接收方確認報文段:SYN = 1,ACK = 1,seq = y,ack = x + 1。
發(fā)送方確認報文段:ACK = 1,seq = x + 1,ack = y + 1。這里可帶數(shù)據(jù)了。
TCP三路握手過程的狀態(tài)變遷:
CLOSED:起始點,在超時或者連接關(guān)閉時候進入此狀態(tài),這并不是一個真正的狀態(tài),而是這個狀態(tài)圖的假想起點和終點。
LISTEN:服務(wù)器端等待連接的狀態(tài)。服務(wù)器經(jīng)過?socket,bind,listen?函數(shù)之后進入此狀態(tài),開始監(jiān)聽客戶端發(fā)過來的連接請求。此稱為應(yīng)用程序被動打開(等到客戶端連接請求)。
SYN_SENT:第一次握手發(fā)生階段,客戶端發(fā)起連接??蛻舳苏{(diào)用?connect,發(fā)送?SYN?給服務(wù)器端,然后進入?SYN_SENT?狀態(tài),等待服務(wù)器端確認(三次握手中的第二個報文)。如果服務(wù)器端不能連接,則直接進入CLOSED狀態(tài)。
SYN_RCVD:第二次握手發(fā)生階段,跟?3?對應(yīng),這里是服務(wù)器端接收到了客戶端的?SYN,此時服務(wù)器由?LISTEN?進入?SYN_RCVD狀態(tài),同時服務(wù)器端回應(yīng)一個?ACK,然后再發(fā)送一個?SYN?即SYN+ACK?給客戶端。
ESTABLISHED:第三次握手發(fā)生階段,客戶端接收到服務(wù)器端的?ACK?包(ACK,SYN)之后,也會發(fā)送一個?ACK?確認包,客戶端進入?ESTABLISHED?狀態(tài),表明客戶端這邊已經(jīng)準(zhǔn)備好,但TCP?需要兩端都準(zhǔn)備好才可以進行數(shù)據(jù)傳輸。服務(wù)器端收到客戶端的?ACK?之后會從?SYN_RCVD?狀態(tài)轉(zhuǎn)移到?ESTABLISHED?狀態(tài),表明服務(wù)器端也準(zhǔn)備好進行數(shù)據(jù)傳輸了。所以?ESTABLISHED?也可以說是一個數(shù)據(jù)傳送狀態(tài)。
TCP 2次握手行不行?為什么要3次?
為了實現(xiàn)可靠數(shù)據(jù)傳輸,?TCP?協(xié)議的通信雙方, 都必須維護一個序列號, 以標(biāo)識發(fā)送出去的數(shù)據(jù)包中, 哪些是已經(jīng)被對方收到的。 三次握手的過程即是通信雙方相互告知序列號起始值, 并確認對方已經(jīng)收到了序列號起始值的必經(jīng)步驟,如果只是兩次握手, 至多只有連接發(fā)起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認。
TCP四次握手釋放
客戶端連接釋放報文段:只有頭部數(shù)據(jù),但要消耗掉一個序號。FIN = 1,seq = u。
服務(wù)端的確認報文:ACK = 1,seq = v,ack = u + 1。
服務(wù)端連接釋放報文段:ACK = 1,F(xiàn)IN = 1,seq = w,ack = u + 1。
接收端的確認報文段:ACK = 1,seq = u + 1,ack = w + 1
TCP建立連接時,是沒有歷史包袱的,立即就能完成,而 斷開連接時, 客戶端發(fā)送FIN給服務(wù)器時, 服務(wù)器中可能還有數(shù)據(jù)在緩沖區(qū)中沒讀完, 服務(wù)器要把數(shù)據(jù)處理完了再發(fā)FIN , 而客戶端什么時候發(fā)FIN就是代碼層次的問題了。如果服務(wù)器沒有緩沖區(qū)數(shù)據(jù)要處理,服務(wù)端在發(fā)送連接釋放的確認報文段時,也是可以同時發(fā)送連接釋放報文段的。
TCP四次揮手過程的狀態(tài)變遷。
FIN_WAIT_1:第一次揮手。主動關(guān)閉的一方(執(zhí)行主動關(guān)閉的一方既可以是客戶端,也可以是服務(wù)器端,這里以客戶端執(zhí)行主動關(guān)閉為例),終止連接時,發(fā)送 FIN 給對方,然后等待對方返回ACK 。
CLOSE_WAIT:接收到FIN 之后,被動關(guān)閉的一方進入此狀態(tài)。具體動作是接收到 FIN,同時發(fā)送ACK。之所以叫 CLOSE_WAIT 可以理解為被動關(guān)閉的一方此時正在等待上層應(yīng)用程序發(fā)出關(guān)閉連接指令。
FIN_WAIT_2:主動端(這里是客戶端)先執(zhí)行主動關(guān)閉發(fā)送FIN,然后接收到被動方返回的 ACK后進入此狀態(tài)。
LAST_ACK:被動方(服務(wù)器端)發(fā)起關(guān)閉請求,由狀態(tài)2 進入此狀態(tài),具體動作是發(fā)送 FIN給對方,同時在接收到ACK 時進入CLOSED狀態(tài)。
CLOSING:兩邊同時發(fā)起關(guān)閉請求時(即主動方發(fā)送FIN,等待被動方返回ACK,同時被動方也發(fā)送了FIN,主動方接收到了FIN之后,發(fā)送ACK給被動方),主動方會由FIN_WAIT_1 進入此狀態(tài),等待被動方返回ACK。
TIME_WAIT:從狀態(tài)變遷圖會看到,四次揮手操作最后都會經(jīng)過這樣一個狀態(tài)然后進入CLOSED狀態(tài)。
滑動窗口機制
在發(fā)送數(shù)據(jù)時 , 一次性發(fā)送多個報文,相當(dāng)于一個窗口,將窗口內(nèi)的數(shù)據(jù)一次性發(fā)出去, 在發(fā)送的第一條的ACK返回后,窗口就往后滑動,繼續(xù)發(fā)送后續(xù)數(shù)據(jù)。因為為了保證可靠性, 降低了數(shù)據(jù)傳輸?shù)男?。為了補救傳輸數(shù)據(jù)的效率,使用了滑動窗口機制 . 因此滑動窗口是一種效率補救機制。發(fā)送數(shù)據(jù)過程中必然會產(chǎn)生丟包的問題: 在丟包的情況下, 能否保證可靠性呢. 下面分為兩種情況分析 :
丟失的是ACK:如果丟失的是其中某條數(shù)據(jù)的ACK,并不會影響可靠性,因為只要后續(xù)數(shù)據(jù)的ACK收到,就說明前面的的數(shù)據(jù)都已經(jīng)發(fā)送到接收端了,因為累計的特點,后續(xù)的ACK會涵蓋前面的ACK。
丟失的是數(shù)據(jù)包:如果丟失的是其中某個數(shù)據(jù)包( 如 1001-2000)的數(shù)據(jù)包丟失),接收方就會一直向發(fā)送方返回1001的ACK(冗余ACK),在重復(fù)幾次后,發(fā)送方就明白了 是1001-2000的數(shù)據(jù)包丟失,啟動重發(fā)機制,重新發(fā)送 1001-2000的數(shù)據(jù)包。
流量控制機制
流量控制是用來控制滑動窗口的大小的,因為滑動窗口機制提高了數(shù)據(jù)發(fā)送的效率,如果滑動窗口越大,發(fā)送速率越大,但是如果發(fā)送速率過大, 而接收方的接收速率 ,是有限的, 這樣就會導(dǎo)致接收方處理不過來, 導(dǎo)致丟包問題, 這樣就要頻繁的進行重發(fā) , 效率反而更低。流量控制機制是一種保證可靠性的機制。根據(jù)接收方接收速率的大小,確定滑動窗口的大小。那么如何確定接收方的接收速率多大呢?在接收方接收數(shù)據(jù)時,接收方的操作系統(tǒng)內(nèi)核中會有一塊 " 接收緩沖區(qū)",先存放當(dāng)前接收到的數(shù)據(jù),而讀取接收緩沖區(qū)中的存放的數(shù)據(jù)的速度,就是接收速率。接受速率的多快是根據(jù)我們的所寫的代碼的實現(xiàn)方式來確定的,代碼的實現(xiàn)方式 五花八門,就是導(dǎo)致接收的速率也各不相同,直接表示并不好表示。

那接收方如何接收緩沖區(qū)的大小告訴發(fā)送方呢?可以在ACK的報文中帶上這個信息,如果緩沖區(qū)滿了,窗口大小就會變成0,此時發(fā)送方就會停止發(fā)送數(shù)據(jù)。
擁塞控制機制
通信的雙方A和B要完成通信,不單只有這兩個,還有很多的中轉(zhuǎn)站 ( 交換機 \ 路由器) 。此時用接收緩沖區(qū)剩余空間的大小來衡量接收方B的接收速率,那中轉(zhuǎn)站的中轉(zhuǎn)能力又要如何進行衡量呢?這就是擁塞控制。
先以最小的滑動窗口來試探。
如果不丟包,說明網(wǎng)絡(luò)順暢,逐步增大滑動窗口。
放大到一定程度后,速率已經(jīng)比較快了,網(wǎng)絡(luò)出現(xiàn)擁堵,進一步出現(xiàn)丟包的情況,當(dāng)發(fā)現(xiàn)丟包后就要減小滑動窗口。
TCP采用慢啟動(Slow Start)。傳輸輪次:指把發(fā)送窗口內(nèi)可以發(fā)送的數(shù)據(jù)全部發(fā)送并接收到最后一個TCP報文的確認報文這樣一個來回。cwnd:擁塞窗口。通常在一條TCP連接開始時,cwnd被設(shè)置為1個MSS(最大報文段),即cwnd=1,該階段,每當(dāng)TCP發(fā)送方將發(fā)送窗口的數(shù)據(jù)發(fā)送完,并順利接收到所有的確認后,就會將擁塞窗口大小翻倍,也即慢啟動階段,cwnd以指數(shù)形式增長,擁塞窗口會一直增長直到到達慢開始門限ssthresh,開始執(zhí)行擁塞避免算法。該階段的擁塞窗口變?yōu)榫€性增長,每次cwnd+1,也即每次增加一個MSS。