TCP Close_Wait問題

[size=large][b]生成原因[/b][/size]:
如果我們的Client程序處於CLOSE_WAIT狀態的話,說明Socket是被動關閉的.
因爲如果是Server端主動斷掉當前連接的話,那麼雙方關閉這個TCP連接共需要四個packet:
Server ---> FIN ---> Client
Server <--- ACK <--- Client
這時候Server端處於FIN_WAIT_2狀態,也就是傳說中的半連接狀態;而我們的程序處於CLOSE_WAIT狀態。
Server <--- FIN <--- Client
這時Client發送FIN給Server,Client就置爲LAST_ACK狀態。
Server ---> ACK ---> Client
Server迴應了ACK,那麼Client的Socket纔會真正置爲CLOSED狀態

既然如此,我們的程序處於CLOSE_WAIT狀態,而不是LAST_ACK狀態,說明還沒有發FIN給Server,那麼可能是在關閉連接之前還有許多數據要發送或者其他事要做,導致沒有發FIN

[size=large][b]原因分析[/b][/size]:
1. 難道Client根本不知道自己收到了FIN包
當Server調用closesocket的時候,Client正在read中,這時候有可能對方發送的FIN包我沒有收到,而是由TCP代回了一個ACK包,所以我這邊套接字進入CLOSE_WAIT狀態.但是我在read的時候,會判斷返回值是否已出錯,是錯誤值的話就主動closesocket,這樣防止沒有接收到FIN包。

2. 爲什麼大量連接都處於CLOSE_WAIT,難道服務器端總是主動斷開連接?
有一種可能就是沒有重用本地地址和端口,一個端口不行,總是換另外一個來用,導致大量的端口進入CLOSE_WAIT狀態。也許我們無法避免被凍結在CLOSE_WAIT狀態永遠不出現,但起碼可以保證不會佔用新的端口。加上類似的代碼保證重用
sc.setReuseAddress(true);


3. 網上查到強行關閉的問題
socket.setSoLinger(boolean on,int linger)

設置SO_LINGER爲0,CloseSocket的時候,不論是否有排隊數據未發送或未被確認。這種關閉方式稱爲“強行關閉”,因爲Socket的虛電路立即被reset,尚未發出的所有數據都會丟失。在遠端的read都會拋出IOException.

4.最終的問題
根據分析得到,只要Client端主動調用了closeSocket,一定會發送FIN包,並改變自己的狀態。因此debug程序代碼,發現問題的真正所在。因爲系統關閉socket是異步調用,在關閉事件執行之前,有異常拋出,導致異步EventHandlerThread死掉,然後事件被丟棄。想辦法fix了這個問題,讓
sc.close();
被真正執行了,一切OK.

[size=large][b]總結[/b][/size]:
當發起主動關閉的左邊這方發送一個FIN過去後,右邊被動關閉的這方要回應一個ACK,這個ACK是TCP迴應的,而不是應用程序發送的,此時,被動關閉的一方就處於CLOSE_WAIT狀態了。如果此時被動關閉的這一方不再繼續調用closesocket,那麼他就不會發送接下來的FIN,導致自己老是處於CLOSE_WAIT。這個時候如果去做read或者write,就會拋出IOException,必須成功調用closesocket,纔會發送一個FIN給主動關閉的這一方,同時也使得自己的狀態變遷爲LAST_ACK。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章