文章目錄
- 三次握手
- 四次揮手
- TIME_WAIT
- CLOSE_WAIT
- 使用wireshark觀察
三次握手
握手的最終目的是主機之間建立連接
首先要有兩個預備知識點
- 三次握手建立連接不一定會成功,其中最擔心的就是最后一次握手失敗,不過會有配套的解決方案
- 建立好連接后是需要被操作系統統一管理起來的。也就是說連接不是隨便一連就完事了,操作系統會經過描述組織后管理起來每一次的連接,又因為維護是需要成本的因而連接并不是建立多少個都可以的
對現實生活來說,都是客戶端主動去向服務端請求連接。
- 第一次握手:客戶端向服務端發送連接請求,TCP報文是有類型的,請求建立連接則TCP報頭的SYN置1,也就是發送SYN類型的報文,代表申請連接。當客戶端發出請求后,此時的狀態為SYN_SENT
- 第二次握手:服務端接收到請求,由于應答機制則會返回應答,且應答中有著同意連接的響應,因而服務端返回的報文類型為SYN和ACK。當客戶端接收到應答時,注意此時的客戶端就已經認為連接成功了,但是服務端并沒有認為連接成功,對于TCP而言兩端是獨立的。當服務端發出應答后,狀態為SYN_REVD
- 第三次握手:客戶端接收到應答后,再向服務端返回一次應答。當服務端接收到應答之后,才會認為連接成功了
那么問題來了,既然三次握手可以建立連接,那么一次,兩次,四次等可不可以呢
- 對于一次和兩次而言:上述提到了建立連接后操作系統需要對每個連接維護起來,那么維護需要成本。如果只有一次握手,那么客戶端每一次發送請求后,服務端一收到就建立連接成功了。那么如果有很多客戶端都發來請求,那么服務端就會承受不住這么多連接了,這就會出現安全問題導致SYN洪水問題。并且一次兩次握手都只能單向的建立連接,不能確保可靠性。一次握手時,客戶端沒有收到服務端的應答,就不知道服務端收到了請求并已經建立好連接了。兩次握手時,服務端沒有收到客戶端的應答,就不知道客戶端已經收到了響應并建立好了連接
- 那么對于三次以上:其實三次以上的握手都是可以的,因為都能確保雙向收到應答確保可靠性。但是這樣建立連接的效率不高,時間會更長,因為網絡通信是需要成本的。
因此,三次握手是可以用最小的成本驗證是否建立好連接,并且可以有效的防止單機進行對服務器的攻擊
四次揮手
揮手的最終目的是:為了斷開主機間的連接
- 第一次揮手:主動斷開連接端向被動斷開連接端發送FIN類型報文,代表不再發送數據,需要斷開連接。
- 第二次揮手:被動斷開連接端收到主動斷開連接端的請求,返回應答,此時被動斷開端為CLOSE_WAIT狀態。
- 第三次揮手:被動斷開端向主動端發送FIN類型報文,代表不再發送數據,斷開連接。此時被動端為LAST_ACK狀態
- 第四次揮手:主動段收到報文后,向被動端發送應答。主動端此時為TIME_WAIT狀態。被動端收到應答后,至此四次揮手完成,連接全部斷開
注意上述的不再發送數據,這里的數據指的是用戶數據,也就是報文中的有效載荷。因為底層還是會交互管理的報文,只不過是上層會關閉掉套接字文件
主動斷開連接端揮手的最終的狀態為TIME_WAIT。被動斷開連接段兩次揮手后會變為CLOSE_WAIT狀態
和雙方誰是客戶端誰是服務端沒有關系,在TCP看來兩端的地位是平等的
TIME_WAIT
主動斷開連接端在揮手完成后需要保持一段時間的TIME_WAIT狀態,為什么呢?
- 為了盡量確保最后一次的ACK被對方收到
- IME_WAIT狀態允許操作系統完全釋放與TCP連接相關的資源,比如可以在釋放該連接的所有相關資源之前確保該連接在網絡中的所有數據已經被刪除
- 如果一個新的TCP連接想要使用與之前CLOSED狀態的連接相同的源IP地址和源端口號,而且該連接的目的IP地址和目的端口號與剛關閉的連接相同,那么TIME_WAIT狀態可以防止舊連接的數據干擾新連接的數據。
TIME_WAIT狀態一般會維持 2 * MSL的時間
MSL:報文的最大壽命,以防止TCP報文在網絡中循環傳遞,避免網絡擁塞或因網絡故障導致報文無法到達目的地。根據操作系統的 不同值不同,在Centos7中默認為60s。
因此如果是服務器在連接過程中關閉了進程,那么就是服務器主動斷開連接,此時是不能夠立即重啟該端口的。但是也有辦法可以立即重啟
調用setsockopt接口,設置socket描述符的選項SO_REUSEADDR為1, 表示允許創建端口號相同但IP地址不同的多個socket描述符
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
CLOSE_WAIT
CLOSE_WAIT狀態的維持是非常短暫的,只要被動斷開端發出斷開請求后,這個狀態就沒有了,也就是第三次揮手。
如果服務器中出現大量的CLOSE_WAIT,這種情況可能是因為
- 出現bug,沒有做出關閉套接字文件描述符
- 服務器壓力太大,可能一直在給客戶端推送數據,來不及關閉文件
如果這個狀態出現的太久,可能會導致資源浪費和連接數量上限的問題。
使用wireshark觀察
首先觀察握手過程
再看看揮手過程
為什么會只出現3次揮手呢?
實際上是因為TCP具有捎帶應答機制,導致第二和第三次揮手重疊在一起了。