TCP协议-握手与挥手
?
認(rèn)識(shí)TCP協(xié)議
TCP全稱(chēng)為“傳輸控制協(xié)議”,這是傳輸層的一個(gè)協(xié)議,對(duì)數(shù)據(jù)的傳輸進(jìn)行一個(gè)詳細(xì)的控制。?
特點(diǎn):
- 面向字節(jié)流
- 安全可靠
- 面向連接
TCP協(xié)議段格式
- 源端口號(hào)與目的端口號(hào):這里與UDP的一樣,每個(gè)數(shù)據(jù)都要知道從哪個(gè)進(jìn)程來(lái),要到哪個(gè)進(jìn)程去。
- 32位序號(hào)與32位確認(rèn)序號(hào):這里的序號(hào)與確認(rèn)信號(hào)可以理解成兩個(gè)通信進(jìn)程在收發(fā)數(shù)據(jù)的時(shí)候互相答復(fù)的信息。比如說(shuō):A進(jìn)程從序列號(hào)1000開(kāi)始給B進(jìn)程發(fā)送數(shù)據(jù),發(fā)送五個(gè)數(shù)據(jù)。那么在B收到數(shù)據(jù)回復(fù)的時(shí)候,這里A的確認(rèn)序列號(hào)應(yīng)該是從1006,如果不是1006,比如說(shuō)是1003,那就意味著1004、1005數(shù)據(jù)包B沒(méi)有收到,于是A啟動(dòng)重發(fā)機(jī)制。這也就保證了數(shù)據(jù)的可靠性,也是TCP的特點(diǎn)之一。序列號(hào)是進(jìn)程發(fā)送消息的號(hào)碼,而確認(rèn)是期望目的進(jìn)程返回的號(hào)碼。進(jìn)行比對(duì),從而驗(yàn)證數(shù)據(jù)包是否到達(dá)。
- 4位TCP報(bào)頭長(zhǎng)度:這里的四位TCP報(bào)頭長(zhǎng)度,可以理解成四個(gè)比特位表示長(zhǎng)度,四位比特位表示的值乘以四就是該TCP頭部的長(zhǎng)度。由圖可知,報(bào)頭最短長(zhǎng)度為20字節(jié),也就是說(shuō)這里的四位TCP報(bào)頭長(zhǎng)度默認(rèn)為0101。并且TCP報(bào)頭長(zhǎng)度不可超過(guò)15*4=60個(gè)字節(jié)。
- 6位標(biāo)志位:①URG:緊急指針是否有效②ACK:確認(rèn)號(hào)是否有效③PSH:提示接收端應(yīng)用程序立刻從TCP緩沖區(qū)內(nèi)部把數(shù)據(jù)讀走④RST:對(duì)方要求重新建立連接,我們把攜帶RST標(biāo)識(shí)的稱(chēng)為復(fù)位報(bào)文段⑤SYN:請(qǐng)求建立連接,我們把攜帶SYN標(biāo)識(shí)的稱(chēng)為同步報(bào)文段⑥FIN:通知對(duì)方,本端要關(guān)閉了,我們把攜帶FIN標(biāo)識(shí)的稱(chēng)為結(jié)束報(bào)文段。
- 16位窗口大小:這里的窗口大小可以看做一個(gè)標(biāo)志,標(biāo)志著TCP緩沖區(qū)內(nèi)部剩余空間的大小。起到一個(gè)流量控制的作用。如果16為窗口滿(mǎn)了,那么這個(gè)時(shí)候是不允許數(shù)據(jù)接收的。后面到達(dá)的數(shù)據(jù)會(huì)被丟失。
- 16位校驗(yàn)和:這里的校驗(yàn)和由發(fā)送端填充,CRC校驗(yàn)。接收端校驗(yàn)數(shù)據(jù)的時(shí)候如果校驗(yàn)不通過(guò),那么認(rèn)為數(shù)據(jù)有問(wèn)題。此處的校驗(yàn)和不僅僅校驗(yàn)TCP首部,還校驗(yàn)數(shù)據(jù)部分。
- 16位緊急指針:標(biāo)識(shí)哪部分的數(shù)據(jù)為緊急數(shù)據(jù)。
連接過(guò)程
我們知道,TCP協(xié)議是面向連接的,也就是說(shuō)它需要客戶(hù)端與服務(wù)器成功連接之后才可使用。那么客戶(hù)端與服務(wù)器的連接過(guò)程是怎樣的呢?簡(jiǎn)單的來(lái)說(shuō)就是三次握手,四次揮手,大意為客戶(hù)端連接服務(wù)器需要三次握手,通信完畢過(guò)后斷開(kāi)連接需要四次揮手。
-
三次握手
?
客戶(hù)端與服務(wù)器在握手之前都做了一些前期的準(zhǔn)備。服務(wù)器在開(kāi)始先分配一個(gè)描述符,然后填充一下sockaddr_in結(jié)構(gòu)體,綁定創(chuàng)建的文件描述符及服務(wù)器端口,接著listen監(jiān)聽(tīng),使得剛才的文件描述符成為一個(gè)監(jiān)聽(tīng)描述符,最后阻塞至accept等待客戶(hù)端的連接。而客戶(hù)端相較來(lái)說(shuō)簡(jiǎn)單一些,就是分配文件描述符,填充sockaddr_in結(jié)構(gòu)體,最后進(jìn)行connect請(qǐng)求服務(wù)器的連接,直至服務(wù)器響應(yīng)。
在客戶(hù)端經(jīng)過(guò)connect請(qǐng)求服務(wù)器響應(yīng)的時(shí)候,向服務(wù)器發(fā)送同步報(bào)文段也就是SYN請(qǐng)求,發(fā)送完畢后等待服務(wù)器響應(yīng)。服務(wù)器如果收到了SYN同步報(bào)文段,那么就會(huì)給客戶(hù)端發(fā)送ACK響應(yīng),意為收到了客戶(hù)端發(fā)送的同步報(bào)文段,在此同時(shí),服務(wù)器也會(huì)發(fā)送SYN同步報(bào)文段,請(qǐng)求客戶(hù)端的響應(yīng)。客戶(hù)端在接收到SYN同步報(bào)文段后也會(huì)發(fā)送ACK響應(yīng)來(lái)回復(fù)服務(wù)器。這個(gè)過(guò)程就是三次握手的過(guò)程。
這樣看來(lái),客戶(hù)端與服務(wù)器的連接是雙方的,兩者都得發(fā)送請(qǐng)求同樣兩者也都得響應(yīng)。圖上來(lái)看,SYN_SENT就是請(qǐng)求連接狀態(tài),SYN_RCVD就是等待連接狀態(tài)。在三次握手成功后,服務(wù)器與客戶(hù)端都會(huì)進(jìn)入ESTABLISHED狀態(tài),也就是TCP連接成功態(tài),這個(gè)時(shí)候就可以進(jìn)行數(shù)據(jù)的傳送了。
這個(gè)過(guò)程中如果客戶(hù)端的SYN請(qǐng)求如果丟包,服務(wù)器不會(huì)響應(yīng),而客戶(hù)端會(huì)有一個(gè)等待時(shí)間,等待時(shí)間到達(dá),未收到ACK響應(yīng),這個(gè)時(shí)候客戶(hù)端會(huì)發(fā)起再次請(qǐng)求。如果多次請(qǐng)求都未成功,此時(shí)客戶(hù)端可能會(huì)判斷網(wǎng)絡(luò)異常,不會(huì)再次請(qǐng)求。同樣再服務(wù)器接收到客戶(hù)端的SYN請(qǐng)求之后也會(huì)發(fā)送ACK響應(yīng)同時(shí)發(fā)送SYN請(qǐng)求。如果客戶(hù)端遲遲不給服務(wù)器ACK響應(yīng),服務(wù)器也會(huì)進(jìn)行重發(fā),直至判斷網(wǎng)絡(luò)異常。所以說(shuō),三次握手任意一個(gè)缺失都不會(huì)連接成功,也就無(wú)法通信。所以三次握手也是確保TCP可靠的一種方式。
三次握手的目的是什么?
答:個(gè)人理解簡(jiǎn)單易懂的就是同步連接雙方的序列號(hào)和確認(rèn)號(hào),并交換tcp窗口的大小信息。
為什么需要二次握手就能完成解決反而需要三次呢?
答:需要三次握手是為了防止已經(jīng)失效的連接請(qǐng)求報(bào)文段又突然傳送到服務(wù)端,因而產(chǎn)生錯(cuò)誤。
四次揮手
在客戶(hù)端與服務(wù)器數(shù)據(jù)傳輸完畢之后,客戶(hù)端沒(méi)有請(qǐng)求了,所以此時(shí)調(diào)用close關(guān)閉文件描述符,開(kāi)始進(jìn)入FIN_WAIT_1狀態(tài),同時(shí)向服務(wù)器發(fā)送FIN結(jié)束報(bào)文段。等待服務(wù)器的響應(yīng)。當(dāng)服務(wù)器這里收到了FIN結(jié)束報(bào)文段時(shí),這個(gè)時(shí)候,服務(wù)器進(jìn)入CLOSE_WAIT狀態(tài)。并給客戶(hù)端進(jìn)行應(yīng)答發(fā)送ACK。當(dāng)客戶(hù)端收到服務(wù)器的ACK響應(yīng)時(shí),進(jìn)入FIN_WAIT_2狀態(tài)。當(dāng)服務(wù)器調(diào)用close時(shí),會(huì)向客戶(hù)端發(fā)送FIN結(jié)束報(bào)文段。此時(shí)進(jìn)入LAST_ACK狀態(tài)。此時(shí)的客戶(hù)端收到服務(wù)器發(fā)送的FIN時(shí),會(huì)向服務(wù)器進(jìn)行響應(yīng)ACK,并且客戶(hù)端進(jìn)入TIME_WAIT狀態(tài),TIME_WAIT結(jié)束之后,進(jìn)入CLOSED,斷開(kāi)連接成功。當(dāng)服務(wù)器收到客戶(hù)端最后的ACK時(shí),進(jìn)入CLOSED狀態(tài)。斷開(kāi)連接成功。
CLOSE_WAIT與LAST_ACK狀態(tài)
在三次握手的時(shí)候服務(wù)器可以將SYN與ACK同時(shí)發(fā)送,但是為什么這里服務(wù)器發(fā)送的FIN與ACK是分開(kāi)的發(fā)送的呢??其實(shí)是這樣的。?
首先FIN信號(hào)是由于調(diào)用close所以才發(fā)送的。而客戶(hù)端調(diào)用close時(shí),發(fā)送FIN結(jié)束報(bào)文段并進(jìn)入FIN_WAIT_1狀態(tài)。而這個(gè)報(bào)文段在服務(wù)器中用戶(hù)態(tài)其實(shí)是無(wú)法感知的,內(nèi)核會(huì)自己處理這個(gè)報(bào)文段,也就是說(shuō)由內(nèi)核進(jìn)行ACK響應(yīng)。這個(gè)過(guò)程中不是由用戶(hù)代碼決定的,服務(wù)器的FIN是由用戶(hù)代碼調(diào)用close發(fā)送的,所以?xún)?nèi)核與服務(wù)器不一定是同時(shí)處理這個(gè)信息的。所以FIN與ACK不一定是同時(shí)發(fā)送出去的。注意:這里是不一定!!!但是三次握手的時(shí)候發(fā)送SYN是由內(nèi)核直接完成的,所以這就可以達(dá)到一個(gè)同步發(fā)送的情況。
如果服務(wù)器的代碼沒(méi)有調(diào)用close,那么意味著并沒(méi)有發(fā)送FIN結(jié)束報(bào)文段。那么也就是說(shuō),此連接的服務(wù)器長(zhǎng)期保持在CLOSE_WAIT狀態(tài),這會(huì)有什么影響??
服務(wù)器長(zhǎng)期保持在CLOSE_WAIT狀態(tài),也就是說(shuō)分配的文件描述符并沒(méi)有關(guān)閉并歸還。那么大量的CLOSE_WAIT存在的話,就會(huì)導(dǎo)致一種資源的泄漏。可能到最后就沒(méi)有可分配的文件描述符了,那么就會(huì)使一些客戶(hù)端無(wú)法連接,從而造成不可估量的影響。
TIME_WAIT
在客戶(hù)端最后一次發(fā)送ACK響應(yīng)后,進(jìn)入TIME_WAIT狀態(tài),而這個(gè)狀態(tài)的時(shí)候客戶(hù)端在做什么呢??
答案就是等待!在客戶(hù)端最后發(fā)送ACK響應(yīng)后,進(jìn)入TIME_WAIT狀態(tài),這是為了防止最后發(fā)送的ACK響應(yīng)丟包。在這里,TIME_WAIT狀態(tài)會(huì)等待2MSL的時(shí)間。
這里的單位 MSL就是 Max Segment Life意思就是報(bào)文的最大生存時(shí)間,這里的生存時(shí)間指的是一個(gè)報(bào)文從發(fā)生到被接收到的整個(gè)過(guò)程,這個(gè)過(guò)程的時(shí)間就是MSL。?
在Linux下可以利用cat /proc/sys/net/ipv4/tcp_fin_timeout來(lái)查看MSL的值。?
而客戶(hù)端最后一次發(fā)送ACK響應(yīng)后,為什么要等待2MSL呢?
這是為了確保最后一條ACK消息的到達(dá)。因?yàn)榭蛻?hù)端在發(fā)送最后一條ACK響應(yīng)后進(jìn)入TIME_WAIT狀態(tài),如果這條ACK報(bào)文丟失,那么服務(wù)器在等待一個(gè)MSL的時(shí)間過(guò)后發(fā)現(xiàn)沒(méi)有收到ACK響應(yīng),那么它會(huì)重新發(fā)送一條FIN報(bào)文。這樣一條ACK響應(yīng)的時(shí)間加上重發(fā)的FIN的時(shí)間正好就是2MSL。如果客戶(hù)端等待2MSL后沒(méi)有收到FIN報(bào)文,那么意味著服務(wù)器收到了客戶(hù)端發(fā)送的ACK報(bào)文,這樣就斷開(kāi)連接。?
這里可以看到客戶(hù)端退出后,進(jìn)入到了TIME_WAIT狀態(tài)。
也就是說(shuō),在TIME_WAIT的時(shí)候,客戶(hù)端與服務(wù)器之間的TCP連接還是存在的。?
在某些情況下服務(wù)器也有可能請(qǐng)求斷開(kāi)連接,由服務(wù)器先進(jìn)入FIN_WAIT_1,這樣的話最終就是服務(wù)器進(jìn)入TIME_WAIT狀態(tài)。那么在這個(gè)狀態(tài)下會(huì)有什么問(wèn)題呢?
這里,我們終止掉服務(wù)器,發(fā)現(xiàn)服務(wù)器進(jìn)入了TIME_WAIT狀態(tài)。這時(shí)候接著再次啟動(dòng)服務(wù)器,發(fā)現(xiàn)啟動(dòng)不了。此時(shí)啟動(dòng)不了的原因的端口號(hào)綁定失敗。這是為什么?
其實(shí)這就是因?yàn)樵赥IME_WAIT狀態(tài)的時(shí)候,TCP連接還是存在的,那么剛才的端口號(hào)仍舊被綁定著。再次啟動(dòng)服務(wù)器的時(shí)候,此時(shí)的端口號(hào)并未被釋放。所以才提示綁定失敗。
如果服務(wù)器需要處理大量的客戶(hù)端連接,每個(gè)連接的生存時(shí)間很短,但是每秒都有大量的客戶(hù)端請(qǐng)求。這個(gè)時(shí)候如果服務(wù)器主動(dòng)關(guān)閉連接,就會(huì)產(chǎn)生大量的TIME_WAIT連接。由于我們的需求量很大,就會(huì)導(dǎo)致TIME_WAIT的連接數(shù)很多,從而導(dǎo)致服務(wù)器的端口不夠用,無(wú)法處理新的連接。這個(gè)時(shí)候如何解決呢?
這時(shí)候可以利用函數(shù)setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)來(lái)解決這個(gè)問(wèn)題。這個(gè)函數(shù)的作用就是允許創(chuàng)建端口號(hào)相同但是IP地址不同的多個(gè)socket描述符。在socket()與bind()之間調(diào)用即可。
?
總結(jié)
以上是生活随笔為你收集整理的TCP协议-握手与挥手的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: TCP协议-如何保证传输可靠性
- 下一篇: ASOC注册过程