shutdown() doesn't actually close the file descriptor—it just changes its usability. To free a socket descriptor, you need to use close().
shutdown是一種優(yōu)雅地單方向或者雙方向關(guān)閉socket的方法。 而close則立即雙方向強(qiáng)制關(guān)閉socket并釋放相關(guān)資源。
如果有多個進(jìn)程共享一個socket道媚,shutdown影響所有進(jìn)程膝但,而close只影響本進(jìn)程。
以下均基于單進(jìn)程socket镰吆。
服務(wù)端調(diào)用shutdown()
server調(diào)用shutdown()芹助,此時任何后續(xù)的send,recv都是無效的(根據(jù)關(guān)閉發(fā)送還是關(guān)閉接收有所不同)挟纱。shutdown本身并不影響底層羞酗,也就是說,此前發(fā)出的異步send/recv不會返回紊服。其次檀轨,在所有已發(fā)送的包被client確認(rèn)后,server會發(fā)送FIN包給client欺嗤,開始TCP四次揮手過程参萄。
注意不管是關(guān)閉發(fā)送還是關(guān)閉接收,server端均向client端發(fā)送FIN報文煎饼。client 端收到FIN報文后讹挎,并不知道server端以何種方式shutdown,甚至不知道server端是shutdown還是close吆玖。
- client端收到FIN報文之后筒溃,詳見下文敘述......
服務(wù)端調(diào)用close()
通過參數(shù)設(shè)置不同,調(diào)用close會出現(xiàn)如下A沾乘,B兩種情況:
A. 向客戶端發(fā)送一個RST報文怜奖,丟棄本地緩沖區(qū)的未讀數(shù)據(jù),關(guān)閉socket并釋放相關(guān)資源翅阵,此種方式為強(qiáng)制關(guān)閉歪玲。(l_onoff為非0,l_linger為0怎顾,)
B. 向客戶端發(fā)送一個FIN報文读慎,收到client端FIN ACK后,進(jìn)入了FIN_WAIT_2階段槐雾,可參考TCP四次揮手過程夭委,此種方式為優(yōu)雅關(guān)閉。如果在l_linger的時間內(nèi)仍未完成四次揮手募强,則強(qiáng)制關(guān)閉株灸。( l_onoff 為非0,l_linger為非0)
FIN與RST
若server端發(fā)送FIN報文后沒有收到client端的FIN ACK擎值,會兩次重傳FIN報文慌烧,若一直收不到client端的FIN ACK,則會給client端發(fā)送RST信號鸠儿,關(guān)閉socket并釋放資源屹蚊。(不同系統(tǒng)實現(xiàn)可能會不同)
client收到FIN信號后厕氨,再調(diào)用read函數(shù)會返回0。因為FIN的接收汹粤,表明client端以后再無數(shù)據(jù)可以接收命斧,對方發(fā)來FIN,表明對方不在發(fā)送數(shù)據(jù)了嘱兼。
(注意所有FIN及ACK報文均由操作系統(tǒng)自動完成發(fā)送接收)
client收到FIN后国葬,會發(fā)送應(yīng)答ack報文,表明收到server的FIN報文芹壕,server收到ack報文之后汇四,就進(jìn)入了FIN_WAIT_2階段。
根據(jù)tcp協(xié)議踢涌,向一個 FIN_WAIT2 狀態(tài)的 TCP寫入數(shù)據(jù)是沒有問題的通孽,所以此時client可以調(diào)用write函數(shù),寫入到發(fā)送緩沖區(qū)睁壁,并由tcp連接利虫,發(fā)送到server的接收緩沖區(qū)。由于server端已經(jīng)關(guān)閉了socket堡僻,所以此時的server接收緩沖區(qū)的內(nèi)容都被拋棄,同時server端返回RST給客戶端疫剃。
client端如何知道已經(jīng)接收到RST報文钉疫?
server發(fā)送RST報文后,并不等待從client端接收任何ack響應(yīng)巢价,直接關(guān)閉socket牲阁。而client端收到RST報文后,也不會產(chǎn)生任何響應(yīng)壤躲。client端收到RST報文后城菊,程序行為如下:
- 阻塞模型下,內(nèi)核無法主動通知應(yīng)用層出錯碉克,只有應(yīng)用層主動調(diào)用read()或者write()這樣的IO系統(tǒng)調(diào)用時凌唬,內(nèi)核才會利用出錯來通知應(yīng)用層對端已經(jīng)發(fā)送RST報文。
- 非阻塞模型下漏麦,select或者epoll會返回sockfd可讀,應(yīng)用層對其進(jìn)行讀取時客税,read()會報RST錯誤。
通過read write函數(shù)出錯返回后撕贞,獲取errno來確定對端是否發(fā)送RST信號更耻。
- client收到RST報文后應(yīng)如何處理?
client端收到RST信號后捏膨,如果調(diào)用read函數(shù)讀取秧均,則會返回RST錯誤食侮。在已經(jīng)產(chǎn)生RST錯誤的情況下,繼續(xù)調(diào)用write目胡,則會發(fā)生epipe錯誤锯七。此時內(nèi)核將向客戶進(jìn)程發(fā)送 SIGPIPE 信號,該信號默認(rèn)會使進(jìn)程終止讶隐,通常程序會異常退出(未處理SIGPIPE信號的情況下)起胰。
在收到server發(fā)送RST報文的情況下,client端的任何read write都是毫無意義的巫延。