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