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