sever如何避免2MSL

一、

根據TCP協議,主動發起關閉的一方,會進入TIME_WAIT狀態,持續2MSL,RFC 793建議MSL設置爲兩分鐘。


爲什麼time_wait需要2*MSL等待時間?


MSL就是maximum segment lifetime(最大分節生命期),這是一個IP數據包能在互聯網上生存的最長時間,超過這個時間將在網絡中消失。

假設最終的 ACK 丟失 , server 將重發 FIN , client 必須維護 TCP 狀態信息以便可以重發最終的 ACK ,否則會發送RST ,結果 server 認爲發生錯誤。 
若要TCP可靠地終止連接的兩個方向 ( 全雙工關閉 ) , client 必須進 TIME_WAIT狀態。
現在我們考慮終止連接時的被動方發送了一個FIN,然後主動方回覆了一個ACK,然而這個ACK可能會丟失,這會造成被動方重發FIN,這個FIN可能會在互聯網上存活MSL。
如果沒有TIME_WAIT的話,假設連接1已經斷開,然而其被動方最後重發的那個FIN(或者FIN之前發送的任何TCP分段)還在網絡上,然而連接
2被建立起來,使用的IP地址與端口與先前的完全相同,後建立的連接又稱作是原先連接的一個化身。原先的連接中有數據報(FIN)殘存於網絡之中,這樣新的連接收到的數據報中有可能是先前連接的數據報。爲了防止這一點,TCP不允許從處於TIME_WAIT狀態的socket建立一個連接。處於TIME_WAIT狀態的socket在等待兩倍的MSL時間以後,將會轉變爲CLOSED狀態。這就意味着,一個成功建立的連接,必然使得先前網絡中殘餘的數據報都丟失了。


對於基於TCP的HTTP協議,如果關閉TCP連接的是Server端,這樣,Server端會進入TIME_WAIT狀態,對於訪問量大的Web Server,會存在大量的TIME_WAIT狀態,假如server一秒鐘接收1000個請求,那麼就會積壓240*1000=240000個TIME_WAIT的記錄,維護這些狀態給Server帶來很大的負擔,並且由於TIME_WAIT

狀態佔用的一些端口由於還沒有被釋放可能還會導致服務器端口不夠用。當然現代操作系統都會用快速

的查找算法來管理這些TIME_WAIT,所以對於新的TCP連接請求,判斷系統中是否有一個TIME_WAIT不會太費時間,但是有這麼多狀態要維護總是不好。


TIME_WAIT 狀態到底會佔用什麼?

被佔用的是一個五元組:(協議,本地IP,本地端口,遠程IP,遠程端口)。對於 Web 服務器,協議是 TCP,本地 IP 通常也只有一個,本地端口默認的 80 或者 443。只剩下遠程 IP 和遠程端口可以變了。如果遠程 IP 是相同的話,就只有遠程端口可以變了。這個只有幾萬個,所以當同一客戶端向服務器建立了大量連接之後,會耗盡可用的五元組導致問題。


在高併發短連接的server端,當server處理完client的請求後立刻close socket此時會出現TIME_WAIT狀

態然後如果client再併發2000個連接,此時部分連接就連接不上了。


如何消除大量TCP短連接引發的TIME_WAIT?


1)可以改爲長連接,但代價較大,長連接太多會導致服務器性能問題,而且PHP等腳本語言,需要通過proxy之類的軟件才能實現長連接;
2)修改ipv4.ip_local_port_range,增大可用端口範圍,但只能緩解問題,不能根本解決問題;
3)客戶端程序中設置socket的SO_LINGER選項;
4)客戶端機器打開tcp_tw_recycle和tcp_timestamps選項;
5)客戶端機器打開tcp_tw_reuse和tcp_timestamps選項;
6)客戶端機器設置tcp_max_tw_buckets爲一個很小的值


So_linger的作用
struct linger {
     int l_onoff; /* 0 = off, nozero = on */
   
 int l_linger; /* linger time */
};
其取值和處理如下:


1、設置 l_onoff爲0,則該選項關閉,l_linger的值被忽略,等於內核缺省情況,close調用會立即返回給調用者,如果可能將會傳輸任何未發送的數據;
2、設置 l_onoff !=0 && l_linger = 0,則套接口關閉時TCP將斷開連接,TCP將丟棄保留在套接口發送緩衝區中的任何數據併發送一個RST給對方,而不是通常的四分組終止序列,這避免了TIME_WAIT狀態;
3、設置 l_onoff != 0 && l_linger != 0,當套接口關閉時內核將拖延一段時間(由l_linger決定)


1、 若設置了SO_LINGER(亦即linger結構中的l_onoff域設爲非零),並設置了零超時間隔,則closesocket()不被阻塞立即執行,不論是否有排隊數據未發送或未被確認。這種關閉方式稱爲 “強制”或“失效”關閉 ,因爲套接口的虛電路立即被複位,且丟失了未發送的數據。在遠端的recv()調用將以WSAECONNRESET出錯。 
2、 若設置了SO_LINGER並確定了非零的超時間隔,則closesocket()調用阻塞進程,直到所剩數據發送完畢或超時。這種關閉稱爲 “優雅”或“從容”關閉 。請注意如果套接口置爲非阻塞且SO_LINGER設爲非零超時,則closesocket()調用將以WSAEWOULDBLOCK錯誤返回。 
3、 若在一個流類套接口上設置了SO_DONTLINGER(也就是說將linger結構的l_onoff域設爲零),則closesocket()調用立即返回。但是,如果可能,排隊的數據將在套接口關閉前發送。請注意,在這種情況下WINDOWS套接口實現將在一段不確定的時間內保留套接口以及其他資源,這對於想用所以套接口的應用程序來說有一定影響。 


TCP要保證在所有可能的情況下使得所有的數據都能夠被投遞。當你關閉一個socket時,主動關閉一端的socket將進入TIME_WAIT狀態,而被動關閉一方則轉入CLOSED狀態,這的確能夠保證所有的數據都被傳輸。當一個socket關閉的時候,是通過兩端互發信息的四次握手過程完成的,當一端調用close()時,就說明本端沒有數據再要發送了。這好似看來在握手完成以後,socket就都應該處於關閉CLOSED狀態了。但這有兩個問題,首先,我們沒有任何機制保證最後的一個ACK能夠正常傳輸,第二,網絡上仍然有可能有殘餘的數據包(wandering duplicates),我們也必須能夠正常處理。


通過正確的狀態機,我們知道雙方的關閉過程如下


假設最後一個ACK丟失了,服務器會重發它發送的最後一個FIN,所以客戶端必須維持一個狀態信息,以便能夠重發ACK;如果不維持這種狀態,客戶端在接收到FIN後將會響應一個RST,服務器端接收到RST後會認爲這是一個錯誤。如果TCP協議能夠正常完成必要的操作而終止雙方的數據流傳輸,就必須完全正確的傳輸四次握手的四個節,不能有任何的丟失。這就是爲什麼socket在關閉後,仍然處於 TIME_WAIT狀態,因爲他要等待以便重發ACK。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章