socket連結的關閉close和shutdown的區別
阿新 • • 發佈:2019-02-06
TCP主動關閉連線
appl: close(), --> FIN FIN_WAIT_1 //主動關閉socket方,呼叫close關閉socket,發FIN
<-- ACK FIN_WAIT_2 //對方作業系統的TCP層,給ACK響應。然後給FIN
<-- FIN
--> ACK "TIME_WAIT" -- 2MSL timeout-->CLOSED
//TIME_WAIT,防止ACK沒有給到對方。
注意:close時,如果TCP傳送佇列中還有資料,那麼將會發送RST包而不是FIN包。
另外:對於Linux下來說,無論是FIN還是RST,應用層read將會返回0,可以認為對方請求關閉連結,呼叫close關閉fd即可。
TCP被動關閉連線
<-- FIN "CLOSE_WAIT" //被動方,收到對方的FIN,處於CLOSE_WAIT狀態
--> ACK //被動方的TCP層,給ACK響應
appl: close(), --> FIN LAST_ACK
//被動方呼叫close,從CLOSE_WAIT轉到LAST_ACK
不調close,將一直在CLOSE_WAIT狀態。
<-- ACK --> CLOSED
tcp是全雙工::
close()會關閉讀寫。
shutdown()可以選擇性的關閉讀、寫或讀寫 。
主動關係SOCKET連結的一方,會進入TIME_WAIT(作用是防止最後一個ACK包丟失)
TIME_WAIT的時間會非常長,因此server儘量減少主動關閉連線。
close和shutdown的區別:
int close(int sockfd);
close(fd)呼叫會將描述字的引用計數減1,只有當socket描述符的引用計數為0時,才關閉socket,即傳送FIN包,因此,在fork()模式中,父程序在accept()返回後,fork()子程序,由子程序處理connfd,而父程序將close(connfd);由於connfd這個socket描述符的引用計數不為0,因此並不引發FIN,所以就沒有關閉和客戶端的連線。
int shutdown(int sockfd, int howto);
// howto: SHUT_RD, SHUT_WR, SHUT_RDWR
shutdown()則不管socket描述符的引用計數,而直接發生FIN,因此會直接關閉連結。
shutdown()可控制read/write兩個方向的管道。
SHUT_RD shutdown(sockfd, SHUT_RD);後,來自對端的資料都被確認,然後悄然丟棄。
SHUT_WR half close狀態。
close()引發的4次互動:(這裡的close是client發起的)
client server
FIN_WAIT_1 ---- FIN M ------>
//(Server端作業系統的TCP層(網路協議棧)響應ACK包)
FIN_WAIT_2 <---- ACK M+1---- CLOSE_WAIT
//(這裡必須呼叫close,才能從CLOSE_WAIT到LAST_ACK)
TIME_WAIT <------ FIN N ----- LAST_ACK
//(TIME_WAIT有一個重要的作用就是防止最後一個ACK丟失)
------- ACK N+1 ----> CLOSE
TIME_WAIT 是主動關閉連結時形成的,等待2MSL時間,約4分鐘。
主要是防止最後一個ACK丟失。 由於time_wait的時間會非常長,因此server端應儘量減少主動關閉連線
CLOSE_WAIT是被動關閉連結是形成的 ,
按狀態機,我方收到FIN,則由TCP實現傳送ACK,因此進入CLOSE_WAIT狀態。
但如果我方不執行close(),就不能由CLOSE_WAIT遷移到LAST_ACK,則系統中會存在很多CLOSE_WAIT狀態的連線。
此時,可能是系統忙於處理讀、寫操作,而未將已收到FIN的連線,進行close。此時,recv/read已收到FIN的連線socket,會返回0。
大量TIME_WAIT和CLOSE_WAIT的存在,會產生怎樣的影響?
核心維護更多的狀態。收到ip包,做hash運算,hlist衝突的概率更大。