TCP/IP学习笔记(七)四次挥手
前面說過,TCP是穩(wěn)定可靠的傳輸層協(xié)議,穩(wěn)定體現(xiàn)在需要先建立連接(三次握手)才可以進行通訊。但是當一方想要關閉連接時,如果它一走了之,另一端又怎么知道連接已經關閉了呢,這就會導致另一端仍然保持著維護連接所需要的一切資源而無法釋放。
所以TCP在關閉連接時也需要進行類似三次握手之類的流程,以通知雙方關閉連接,釋放資源。稱為四次揮手
四次揮手
關閉一個TCP連接需要進行四次報文交互,任意一端關閉連接時都需要向對端發(fā)送報文段。
關閉時發(fā)送的報文段中FIN位被置1,表示關閉連接
首先調用close關閉套接字描述符的一方稱為主動關閉(以客戶端為例),從圖中可以看到
- 客戶端發(fā)送FIN位被置1的報文段,告知服務器自己已經將連接關閉。此時客戶端狀態(tài)變?yōu)镕IN_WAIT1(表示發(fā)送了一個FIN報文段)
- 服務器收到客戶端發(fā)送的FIN報文段后將應答報文段發(fā)給客戶端。此時服務器狀態(tài)變?yōu)镃LOSE_WAIT(表示等待服務器應用程序關閉連接)
- 客戶端收到服務器的應答報文段后狀態(tài)變?yōu)镕IN_WAIT2(表示等待服務器發(fā)送FIN報文段)
- 一段時間后服務器應用程序調用read/recv時返回0,得知客戶端已經關閉連接,隨后向客戶端發(fā)送FIN被置1的報文段。此時服務器狀態(tài)變?yōu)長AST_ACK(表示等待客戶端最后一個應答)
- 客戶端收到服務器的FIN報文段后返回應答報文段。此時客戶端狀態(tài)變?yōu)門IME_WAIT
- 服務器收到客戶端的應答報文段后,連接關閉流程結束
如果客戶端執(zhí)行close(全關閉),那么在FIN_WAIT2狀態(tài)下需要等待對端發(fā)送FIN報文段才能夠繼續(xù)關閉,如果遲遲收不到對端的FIN報文段,將可能一直等下去。不過很多TCP實現(xiàn)上都會啟動一個定時器,如果規(guī)定時間到達后仍然沒有收到對端的FIN報文段,就繼續(xù)關閉
從wireshark中也可以看到連接關閉時的報文段發(fā)送情況,為了使結果更為明顯,執(zhí)行的操作步驟為
- 開啟服務器客戶端程序
- 客戶端等待0.5秒后關閉連接
- 服務器應用程序發(fā)現(xiàn)客戶端關閉后(read/recv返回0),等待1秒后關閉連接
等待0.5秒和1秒的原因是防止延遲ACK的存在使報文段一起發(fā)送,對觀察結果有影響
從結果可以看出
- 1-3行是建立連接時的三次握手
- 4-7依次是客戶端關閉連接,服務器發(fā)送應答,服務器關閉連接,客戶端發(fā)送應答
另外,雖然客戶端關閉連接后直接結束了進程,但是當服務器發(fā)送FIN時仍然能夠響應ACK,可見TCP是獨立于應用程序管理報文段的接收和發(fā)送的
TIME_WAIT狀態(tài)
在上面連接關閉的狀態(tài)轉換圖中可以看到,主動關閉的一方TCP會變?yōu)門IME_WAIT狀態(tài)而不會立即關閉,在測試程序運行結束之后也能夠看到(服務器監(jiān)聽端口為9999)
? client netstat -ant | grep 9999 tcp 0 0 127.0.0.1:45378 127.0.0.1:9999 TIME_WAIT由于主動關閉的是客戶端,所以客戶端使用的端口45378會變?yōu)門IME_WAIT狀態(tài)。在該狀態(tài)下的端口不能夠被立即使用,這么做的好處是
- 應對服務器重傳的FIN報文段。如果服務器沒有接收到最后一個ACK報文,就會重傳FIN報文段,因為客戶端TCP處在TIME_WAIT狀態(tài)下,仍保留著連接信息,可以再次發(fā)送ACK報文給服務器。而如果客戶端TCP直接關閉,那么對于服務器重發(fā)的FIN報文會認為是無效的連接,將返回RST報文段
- 使仍在網絡中的報文段消失。防止使用同樣端口的新連接建立,使服務器誤以為是上一個客戶端。假設沒有TIME_WAIT狀態(tài),上一個客戶端關閉后立即有一個新客戶端使用相同端口并且立刻向服務器發(fā)送報文段(SYN),此時服務器會認為這個報文段是上一個客戶端發(fā)送的之前沒到達但現(xiàn)在剛到達的報文段,由于連接已經關閉,這個報文段將被丟掉
TIME_WAIT時間一般是2倍的報文段最大生存時間(2MSL),MSL的時間根據(jù)實現(xiàn)互不相同,常見的有30秒,1分鐘不等
此外,TCP也提供了可以復用TIME_WAIT端口的方法,為套接字設置SO_REUSEADDR選項即可
TCP狀態(tài)變遷圖
半關閉
由于TCP是全雙工的協(xié)議,任意一端都既可寫又可讀,所以TCP允許某一端執(zhí)行半關閉操作(shutdown函數(shù)),比如只關閉發(fā)送數(shù)據(jù)的通道而保留接收數(shù)據(jù)的通道。
客戶端關閉發(fā)送通道實際上就是告訴服務器數(shù)據(jù)已經發(fā)送完了,接下來都不會再發(fā)送數(shù)據(jù)了,但是可以接收數(shù)據(jù),服務器可以發(fā)過來
而當服務器數(shù)據(jù)發(fā)送完畢后就可以調用close執(zhí)行全關閉操作了,因為雙方都不再發(fā)送數(shù)據(jù),就意味著連接可以終止了
接下來通過wireshark觀察半關閉的報文段發(fā)送情況,執(zhí)行流程為
- 啟動服務器客戶端,建立連接
- 客戶端0.5秒后調用::shutdown(fd, ::SHUT_WR)關閉寫通道,不再發(fā)送數(shù)據(jù)
- 服務器得知后(read/recv返回0)等待0.5秒向客戶端發(fā)送一條數(shù)據(jù)
- 服務器1秒后調用close關閉連接
- 客戶端得知后關閉連接
從圖中可以看到當一端發(fā)送FIN報文段后仍然可以進行數(shù)據(jù)交互
總結
以上是生活随笔為你收集整理的TCP/IP学习笔记(七)四次挥手的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 每天一道LeetCode----从数组中
- 下一篇: 每天一道LeetCode-----从右向