首先我們先來回顧一下tcp關閉連線的過程:
假設a和b連線狀態為est,a需要主動關閉:
a傳送fin給b,並將狀態更改為fin_wait1,
b接收到fin將狀態更改為close_wait,並回覆ack和fin
a收到ack後將狀態更改為fin_wait2,收到fin後,更改狀態為wait_timeout並給b返回ack
b收到ack後,將關閉自己的鏈結close。
問題就在此時,a將處於wait_timeout狀態長達2msl時常(rfc793定義了msl為2分鐘,linux設定成了30s),為什麼會有這個狀態存在呢?為什麼不直接進入close狀態呢?
主要有兩個原因:
1、time_wait確保有足夠的時間讓對端收到了ack,如果被動關閉的那方沒有收到ack,就會觸發被動端重發fin,一來一去正好2個msl;
2、有足夠的時間讓這個連線不會跟後面的連線混在一起(如果連線被重用了,那麼這些延遲收到的包就有可能會跟新連線混在一起)。
我們經常就會在伺服器上發現有很多的wait_timeout,特別常見的就是http伺服器,從上面的描述來看,這個狀態應該是主動關閉的一方,才會有的狀態,我們有沒有想過,就是為什麼伺服器端會主動斷開連線呢?不應該是客戶端主動斷開嗎?
據說,其實在最初的http協議,伺服器在傳送完客戶端需要的內容後,就主動關閉連線,因為當時的客戶端,需要等待所有效果渲染完畢,才會主動斷開連線,為了照顧當時效能低下的伺服器,更好的服務其他客戶,才在協議中這樣規定,說到底就是http協議太老了,所以google才會推他的spdy協議。
那麼,如何解決這種問題呢?
網上有兩種方法,設定tcp_tw_reuse和tcp_tw_recycle,但是這兩種方法據說都存在一些危險性:
具體引用coolshell的blog:
既然都這麼危險,該怎麼解決呢?目前我能想到的就是增加keepalive的時長,等著客戶端來斷開連線,但是有時候瀏覽器可能會設定斷開的時間更長……
其實,time_wait表示的是你主動斷連線,所以,這就是所謂的「不作死不會死」。試想,如果讓對端斷連線,那麼這個破問題就是對方的了,呵呵。另外,如果你的伺服器是於http伺服器,那麼設定乙個http的keepalive有多重要(瀏覽器會重用乙個tcp連線來處理多個http請求),然後讓客戶端去斷鏈結(你要小心,瀏覽器可能會非常貪婪,他們不到萬不得已不會主動斷連線)。不知道大家在伺服器上,是怎麼解決這個問題的?
Tcp主動關閉連線導致TIME WAIT狀態
最近寫了乙個程序,需要通過20個執行緒迴圈600個使用者獲取每乙個使用者的xx資訊,是通過socket連線oracle mdb伺服器獲取的,但是在本機windows上測試發現大量的time wait狀態,按照網上的說法,調了登錄檔的引數,但是無濟於事,socket.setreuseaddress方法...
關閉TCP連線的學問
從tcp協議角度來看,乙個已建立的tcp連線有兩種關閉方式,一種是正常關閉,即四次揮手關閉連線 還有一種則是異常關閉,我們通常稱之為連線重置 reset 首先說一下正常關閉時四次揮手的狀態變遷,關閉連線的主動方狀態變遷是fin wait 1 fin wait 2 time wait,而關閉連線的被動...
關閉TCP連線的學問
從tcp協議角度來看,乙個已建立的tcp連線有兩種關閉方式,一種是正常關閉,即四次揮手關閉連線 還有一種則是異常關閉,我們通常稱之為連線重置 reset 首先說一下正常關閉時四次揮手的狀態變遷,關閉連線的主動方狀態變遷是fin wait 1 fin wait 2 time wait,而關閉連線的被動...