在網(wǎng)絡(luò)傳輸中,傳輸控制協(xié)議(TCP)是傳輸層非常重要的一個(gè)協(xié)議,所以學(xué)習(xí)TCP協(xié)議是很有必要的一件事情。TCP協(xié)議是一種可靠的、一對(duì)一的、面向有連接的一種通信協(xié)議,通常在TCP的網(wǎng)絡(luò)請(qǐng)求中,在獲取到對(duì)應(yīng)的IP地址后,會(huì)以隨機(jī)端口(1024-65535)向服務(wù)器80端口發(fā)起TCP的連接請(qǐng)求,這個(gè)連接會(huì)經(jīng)過TCP/IP協(xié)議棧,最后到達(dá)服務(wù)器。而在建立連接這個(gè)過程中,通過一次三次握手來確定連接的建立。
首先,明確一點(diǎn),因?yàn)榉?wù)器端維護(hù)的只是一個(gè)端口,并沒有主動(dòng)建立連接的能力,所以只有客戶端能主動(dòng)的建立與服務(wù)器端的連接,所以TCP請(qǐng)求的發(fā)起者一定是為客戶端。這里先來看一張圖。
三次握手

我們知道發(fā)起連接的一定是客戶端,那來看看三次握手的詳細(xì)過程。
第一次握手:客戶端發(fā)送SYN報(bào)文到服務(wù)器,服務(wù)器收到SYN報(bào)文之后能確定什么信息呢?信息1->,客戶端發(fā)送能力是正常,信息2->服務(wù)器端明確自己的接收能力是正常。(服務(wù)器端明確的信息)
第二次握手:服務(wù)器端回應(yīng)客戶端的信息,將收到SYN報(bào)文中的seq數(shù)據(jù)加一,將其作為創(chuàng)建的ACK的數(shù)據(jù),再設(shè)置一個(gè)新的Seq數(shù)據(jù),將這個(gè)ACK和SYN的報(bào)文發(fā)送回客戶端。在客戶端收到SYN和ACK報(bào)文之后。會(huì)得到的信息為,信息1->服務(wù)器端的接收能力正常,信息2->服務(wù)器端的發(fā)送能力正常,信息3->客戶端的接收能力正常。(客戶端明確的信息)
第三次握手:客戶端回應(yīng)服務(wù)器端的信息,將收到服務(wù)器SYN信息中的Seq數(shù)據(jù)加一,將其作為新的ACK的數(shù)據(jù)。發(fā)送給服務(wù)器端,服務(wù)器收到ACK信息之后,會(huì)確定的信息。信息1->客戶端的接收和發(fā)送能力正常。信息2->服務(wù)器端的接收和發(fā)送能力正常。(服務(wù)器端明確的信息)
經(jīng)過三次握手之后,建立起雙方之間的連接,三次握手主要的作用是來確定客戶端和服務(wù)器端的發(fā)送和接收能力都正常之后才能建立連接。這里其實(shí)會(huì)有疑問,一次,兩次,四次不可以嘛?根據(jù)作用來說,一次和兩次肯定是不能完全讓客戶端和服務(wù)器端來明確對(duì)方的所有的狀態(tài)信息。而四次的話,又會(huì)造成不必要的資源浪費(fèi),所以三次是最簡(jiǎn)潔明了的,確保兩端都獲取到對(duì)方狀態(tài)信息。
圖片文字只是理論,我們還是需要實(shí)際的行動(dòng)來驗(yàn)證理論,這里來看下使用Wiresshark抓包工具獲取到的信息。

在這里呢,使用nc命令在終端來模擬客戶端連接服務(wù)器,nc -l 6060的為,打開一個(gè)6060的端口。nc 127.0.0.1(IP地址)6060(端口)連接6060端口。左邊的終端可以視作服務(wù)器,右邊的終端視作客戶端。
在抓包工具中這里可以看到四次?不是三次握手嘛?不要著急,看到第四次的信息是TCP窗口更新,所以這個(gè)明確的知道這不是TCP協(xié)議中的握手。
這里看到的是三次的握手,這里主要關(guān)注的幾個(gè)點(diǎn)
SYN:報(bào)文段,不能攜帶數(shù)據(jù)(發(fā)起建立報(bào)文,會(huì)消耗一個(gè)序列號(hào))
Seq:全稱為sequence number,序列號(hào)(第一次隨機(jī)產(chǎn)生,后續(xù)報(bào)文中累加)很重要
ACK:報(bào)文段,可以攜帶數(shù)據(jù)(確認(rèn)報(bào)文,如果攜帶數(shù)據(jù),則會(huì)消耗一個(gè)序列號(hào),如果不攜帶數(shù)據(jù),則不會(huì)消耗序列號(hào)seq)
Ack:是ACK的序列號(hào),根據(jù)SYN報(bào)文中的Seq來生成
第一次握手:[SYN] 為報(bào)文段的類型,可以看到在1中的報(bào)文中的Seq為2812367146,這是一個(gè)隨機(jī)產(chǎn)生的序列號(hào),每一次的報(bào)文都會(huì)有自己的序列號(hào)作為標(biāo)識(shí)。服務(wù)器端接收到這個(gè)SYN報(bào)文之后,知道的信息為,客戶端發(fā)送了一個(gè)建立連接的SYN包,編號(hào)為2812376146。
第二次握手:在2中有兩個(gè)報(bào)文段是[SYN,ACK],SYN報(bào)文的序列號(hào)seq為一個(gè)新隨機(jī)產(chǎn)生的序列號(hào)587195924,而新創(chuàng)建的ACK報(bào)文的Ack序列號(hào)為2812367147。這里的意思是服務(wù)器端發(fā)出的意思是為兩重,第一個(gè)是建立連接的SYN信息,有自己生成的seq。第二個(gè)是確認(rèn)的ACK信息,將第一次握手收到的SYN信息中的Seq+1作為ACK的序列號(hào),告訴客戶端,收到了2812376146這個(gè)序列號(hào)的SYN報(bào)文。
第三次握手:在3中只有一個(gè)ACK的報(bào)文,ACK的Ack序號(hào)為587195925。這里的意思是確認(rèn)了服務(wù)器的SYN包,Ack的序號(hào)為第二次握手中SYN包中Seq+1,因?yàn)檫@里的ACK報(bào)文只是確認(rèn),并沒有攜帶數(shù)據(jù),所以這里的seq還是第二次握手中ACK的序號(hào)。
通過三次握手的連接之后,下一次報(bào)文中的Seq序列號(hào)為第三次ACK報(bào)文中Seq+1
四次揮手
抓包工具的數(shù)據(jù)也驗(yàn)證了理論,所以接下來我們來看一下斷開連接的四次揮手,同樣從理論到驗(yàn)證,如下圖所示

問題:四次揮手一定是客戶端發(fā)起嘛?
答案:這是不一定的,因?yàn)樵赥CP的連接中,所以只要有任何一端發(fā)起了斷開的請(qǐng)求,對(duì)方收到響應(yīng)之后做出斷開請(qǐng)求的四次揮手,即完成了斷開連接。所以斷開連接的四次揮手可以為客戶端發(fā)起,同樣也可以由服務(wù)器端發(fā)起。
上圖中為客戶端發(fā)起斷開連接的步驟,服務(wù)器端發(fā)起是同樣的道理。
第一次揮手:客戶端發(fā)送一個(gè)FIN和ACK的報(bào)文,序列號(hào)seq為傳輸數(shù)據(jù)包中最后一次的Seq+1,ACK的序列號(hào)Ack則為上一次ACK包中的序列號(hào)。將客戶端的狀態(tài)修改為FIN_WAIT1。服務(wù)器收到之后明確信息->客戶端在等待關(guān)閉。
第二次揮手:服務(wù)器將收到FIN報(bào)文中的Seq數(shù)據(jù)+1作為新ACK報(bào)文的Ack,將收到的ACK報(bào)文中的Ack+1作為新報(bào)文ACK的Seq,將服務(wù)器端的狀態(tài)調(diào)整為CLOSE_WAIT,然后將新的確認(rèn)報(bào)文ACK發(fā)送給客戶端。客戶端收到之后明確信息->服務(wù)器端知曉客戶端在等待關(guān)閉,同時(shí)將客戶端的狀態(tài)調(diào)整為FIN_WAIT2。
第三次揮手:服務(wù)器再次的發(fā)起FIN和ACK的報(bào)文,F(xiàn)IN的Seq是最后一次報(bào)文的Seq+1,ACK報(bào)文的Ack是最后一次ACK報(bào)文的的Ack+1,將服務(wù)器端的狀態(tài)修改為L(zhǎng)AST_ACK。客戶端在收到之后明確信息->服務(wù)器端發(fā)送了最后一次,在等待關(guān)閉。
第四次揮手:客戶端發(fā)起ACK報(bào)文,新的ACK報(bào)文的seq為第三次揮手收到的Ack,而Ack為收到FIN中的Seq+1,將自己的狀態(tài)調(diào)整為TIME_WAIT。服務(wù)器端收到之后明確信息->客戶端關(guān)閉,將服務(wù)器端的狀態(tài)調(diào)整為CLOSED。而客戶端在TIME_WAIT狀態(tài)等待2MSL秒之后,將自己的狀態(tài)調(diào)整為CLOSED。
同樣這里使用抓包工具來驗(yàn)證

這里的數(shù)據(jù)就不一一解讀了,因?yàn)樵谶B接之后,沒有做任何的數(shù)據(jù)傳輸,所以這里的Seq和Ack的序號(hào)和連接的時(shí)候是連續(xù)的。
FIN:報(bào)文段,不能攜帶數(shù)據(jù)(關(guān)閉報(bào)文,會(huì)消耗一個(gè)序列號(hào))
這里說明一下兩個(gè)點(diǎn)
1:為什么一定要四次揮手,可以看到,在關(guān)閉的時(shí)候,第二次和第三次揮手都是由被動(dòng)方(圖中為服務(wù)器端)發(fā)起,因?yàn)樵谀承r(shí)候可能傳輸?shù)膱?bào)文還沒有傳輸完成,所以先通知客戶端收到關(guān)閉的請(qǐng)求,然后確認(rèn)服務(wù)器端的傳輸都完成之后,再發(fā)起第三次揮手,告訴客戶端要斷開連接。
2:為什么在最后一次揮手之后要等待2MSL再調(diào)整狀態(tài)為CLOSED,兩個(gè)原因,第一,是需要確定已經(jīng)發(fā)出去,因?yàn)锳CK報(bào)文有可能丟失,會(huì)需要重發(fā)。第二,如果立馬關(guān)閉的話,有可能在下一次連接的是相同的IP和端口,為了防止舊連接和新連接的數(shù)據(jù)發(fā)生混亂,所以設(shè)置等待的時(shí)間讓舊連接的數(shù)據(jù)完全關(guān)閉,這樣就不會(huì)讓舊連接和新連接之間數(shù)據(jù)造成混亂。
保障數(shù)據(jù)包有效
TCP是可靠的數(shù)據(jù)連接,可是只是通過三次握手和四次揮手就一定能保證數(shù)據(jù)的傳輸不會(huì)出現(xiàn)問題嘛?當(dāng)然是不可以,所以TCP通過以下的幾個(gè)控制來確保數(shù)據(jù)包的傳輸有效。
一,校驗(yàn)和
1、首先將檢驗(yàn)和置零;
2、然后將TCP偽首部部分,TCP首部部分,數(shù)據(jù)部分都劃分成16位的一個(gè)個(gè)16進(jìn)制數(shù)
3、將這些數(shù)逐個(gè)相加,記得溢出的部分加到最低位上,這是循環(huán)加法:0xc0a8+ 0x0166+……+0x0402=0x9b49
4、最后將得到的結(jié)果取反,則可以得到檢驗(yàn)和位0x64b6
發(fā)送方:原碼相加 ,并將高位疊加到低位,取反 ,得到反碼求和結(jié)果,放入校驗(yàn)和
接收方:將所有原碼 相加,高位疊加, 如全為1,則正確
發(fā)送的數(shù)據(jù)包的二進(jìn)制相加然后取反,目的是檢測(cè)數(shù)據(jù)在傳輸過程中的任何變化。如果收到報(bào)文段的檢驗(yàn)和有差錯(cuò),TCP將丟棄這個(gè)報(bào)文段和不確認(rèn)收到此報(bào)文段,這時(shí) TCP 發(fā)送數(shù)據(jù)端超時(shí)后會(huì)重發(fā)數(shù)據(jù);
二. 確認(rèn)應(yīng)答+序列號(hào)
序列號(hào):TCP傳輸時(shí)將每個(gè)字節(jié)的數(shù)據(jù)都進(jìn)行了編號(hào),這就是序列號(hào)Seq。
確認(rèn)應(yīng)答:TCP傳輸?shù)倪^程中,每次接收方收到數(shù)據(jù)后,都會(huì)對(duì)傳輸方進(jìn)行確認(rèn)應(yīng)答,也就是發(fā)送ACK報(bào)文。這個(gè)ACK報(bào)文當(dāng)中帶有對(duì)應(yīng)的確認(rèn)序列號(hào),告訴發(fā)送方,接收到了哪些數(shù)據(jù),下一次的數(shù)據(jù)從哪里發(fā)。
序列號(hào)的作用不僅僅是應(yīng)答的作用,有了序列號(hào)能夠?qū)⒔邮盏降臄?shù)據(jù)根據(jù)序列號(hào)排序,并且去掉重復(fù)序列號(hào)的數(shù)據(jù)。
應(yīng)答機(jī)制:當(dāng) TCP 收到發(fā)自 TCP 連接另一端的數(shù)據(jù),它將發(fā)送一個(gè)確認(rèn)(累積確認(rèn):對(duì)所有按序接收的數(shù)據(jù)的確認(rèn))。這個(gè)確認(rèn)不是立即發(fā)送,通常將推遲幾分之一秒
TCP給發(fā)送的每一個(gè)包進(jìn)行編號(hào),接收方對(duì)數(shù)據(jù)包進(jìn)行排序,把有序數(shù)據(jù)傳送給應(yīng)用層。
三,超時(shí)重傳
當(dāng)報(bào)文發(fā)出后在一定的時(shí)間內(nèi)未收到接收方的確認(rèn),發(fā)送方就會(huì)進(jìn)行重傳(通常是在發(fā)出報(bào)文段后設(shè)定一個(gè)鬧鐘,到點(diǎn)了還沒有收到應(yīng)答則進(jìn)行重傳)
接收方獲取到重復(fù)的數(shù)據(jù)之后,利用序列號(hào)識(shí)別到重復(fù)的數(shù)據(jù),將其丟棄,再重新發(fā)送ack報(bào)文
四,流量控制
TCP根據(jù)接收端的處理能力,決定發(fā)送端的發(fā)送速度,TCP通過滑動(dòng)窗口來進(jìn)行流量控制
流量窗口:因?yàn)門CP是全雙工通信,通信的任意一方都會(huì)維護(hù)另一方的接收窗口(即接收方當(dāng)前可用的緩存空間),數(shù)據(jù)是動(dòng)態(tài)變化的,緩存空間也是動(dòng)態(tài)的,也稱之為滑動(dòng)窗口
五,擁塞控制
為了避免由于網(wǎng)絡(luò)擁堵而采取的對(duì)發(fā)送方發(fā)送速率的限制稱為擁塞控制
TCP 通過擁塞窗口實(shí)現(xiàn)擁塞避免->TCP 的每一端除了維護(hù)進(jìn)行流量控制的滑動(dòng)窗口外,還會(huì)維護(hù)一個(gè)擁塞窗口(cwnd),擁塞窗口就是對(duì) TCP 發(fā)送方的發(fā)送速率進(jìn)行限制的,具體來說的就是 TCP 會(huì)控制發(fā)送方發(fā)送到連接中的但是還沒有被接收方確認(rèn)的數(shù)據(jù)量。
通過上述的控制,這樣就給數(shù)據(jù)的傳輸安全有了一定的保障。