http://www.jianshu.com/p/eecab8d50697
shutdown() doesn't actually close the file descriptor—it just changes its usability. To free a socket descriptor, you need to use close().
-
shutdown是一種優雅地單方向或者雙方向關閉socket的方法。 而close則立即雙方向強制關閉socket並釋放相關資源。
-
如果有多個進程共享一個socket,shutdown影響所有進程,而close只影響本進程。
以下均基於單進程socket。
服務端調用shutdown()
-
server調用shutdown(),此時任何后續的send,recv都是無效的(根據關閉發送還是關閉接收有所不同)。shutdown本身並不影響底層,也就是說,此前發出的異步send/recv不會返回。其次,在所有已發送的包被client確認后,server會發送FIN包給client,開始TCP四次揮手過程。
-
注意不管是關閉發送還是關閉接收,server端均向client端發送FIN報文。client 端收到FIN報文后,並不知道server端以何種方式shutdown,甚至不知道server端是shutdown還是close。
- client端收到FIN報文之后,詳見下文敘述......
服務端調用close()
通過參數設置不同,調用close會出現如下A,B兩種情況:
A. 向客戶端發送一個RST報文,丟棄本地緩沖區的未讀數據,關閉socket並釋放相關資源,此種方式為強制關閉。(l_onoff為非0,l_linger為0,)
B. 向客戶端發送一個FIN報文,收到client端FIN ACK后,進入了FIN_WAIT_2階段,可參考TCP四次揮手過程,此種方式為優雅關閉。如果在l_linger的時間內仍未完成四次揮手,則強制關閉。( l_onoff 為非0,l_linger為非0)
FIN與RST
-
若server端發送FIN報文后沒有收到client端的FIN ACK,會兩次重傳FIN報文,若一直收不到client端的FIN ACK,則會給client端發送RST信號,關閉socket並釋放資源。(不同系統實現可能會不同)
-
client收到FIN信號后,再調用read函數會返回0。因為FIN的接收,表明client端以后再無數據可以接收,對方發來FIN,表明對方不在發送數據了。
(注意所有FIN及ACK報文均由操作系統自動完成發送接收)
-
client收到FIN后,會發送應答ack報文,表明收到server的FIN報文,server收到ack報文之后,就進入了FIN_WAIT_2階段。
-
根據tcp協議,向一個 FIN_WAIT2 狀態的 TCP寫入數據是沒有問題的,所以此時client可以調用write函數,寫入到發送緩沖區,並由tcp連接,發送到server的接收緩沖區。由於server端已經關閉了socket,所以此時的server接收緩沖區的內容都被拋棄,同時server端返回RST給客戶端。
-
client端如何知道已經接收到RST報文?
server發送RST報文后,並不等待從client端接收任何ack響應,直接關閉socket。而client端收到RST報文后,也不會產生任何響應。client端收到RST報文后,程序行為如下:
- 阻塞模型下,內核無法主動通知應用層出錯,只有應用層主動調用read()或者write()這樣的IO系統調用時,內核才會利用出錯來通知應用層對端已經發送RST報文。
- 非阻塞模型下,select或者epoll會返回sockfd可讀,應用層對其進行讀取時,read()會報RST錯誤。
通過read write函數出錯返回后,獲取errno來確定對端是否發送RST信號。
- client收到RST報文后應如何處理?
client端收到RST信號后,如果調用read函數讀取,則會返回RST錯誤。在已經產生RST錯誤的情況下,繼續調用write,則會發生epipe錯誤。此時內核將向客戶進程發送 SIGPIPE 信號,該信號默認會使進程終止,通常程序會異常退出(未處理SIGPIPE信號的情況下)。
在收到server發送RST報文的情況下,client端的任何read write都是毫無意義的。
作者:dacheng
鏈接:http://www.jianshu.com/p/eecab8d50697
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。