標籤:
1. TCP串連的狀態
首先介紹一下TCP串連建立與關閉過程中的狀態。TCP串連過程是狀態的轉換,促使狀態發生轉換的因素包括使用者調用、特定資料包以及逾時等,具體狀態如下所示:
- CLOSED:初始狀態,表示沒有任何串連。
- LISTEN:Server端的某個Socket正在監聽來自遠方的TCP連接埠的串連請求。
- SYN_SENT:發送串連請求後等待確認資訊。當用戶端Socket進行Connect串連時,會首先發送SYN包,隨即進入SYN_SENT狀態,然後等待Server端發送三向交握中的第2個包。
- SYN_RECEIVED:收到一個串連請求後回送確認資訊和對等的串連請求,然後等待確認資訊。通常是建立TCP串連的三向交握過程中的一個中間狀態,表示Server端的Socket接收到來自Client的SYN包,並作出回應。
- ESTABLISHED:表示串連已經建立,可以進行資料轉送。
- FIN_WAIT_1:主動關閉串連的一方等待對方返回ACK包。若Socket在ESTABLISHED狀態下主動關閉串連並向對方發送FIN包(表示己方不再有資料需要發送),則進入FIN_WAIT_1狀態,等待對方返回ACK包,此後還能讀取資料,但不能發送資料。在正常情況下,無論對方處於何種狀態,都應該馬上返回ACK包,所以FIN_WAIT_1狀態一般很難見到。
- FIN_WAIT_2:主動關閉串連的一方收到對方返回的ACK包後,等待對方發送FIN包。處於FIN_WAIT_1狀態下的Socket收到了對方返回的ACK包後,便進入FIN_WAIT_2狀態。由於FIN_WAIT_2狀態下的Socket需要等待對方發送的FIN包,所有常常可以看到。若在FIN_WAIT_1狀態下收到對方發送的同時帶有FIN和ACK的包時,則直接進入TIME_WAIT狀態,無須經過FIN_WAIT_2狀態。
- TIME_WAIT:主動關閉串連的一方收到對方發送的FIN包後返回ACK包(表示對方也不再有資料需要發送,此後不能再讀取或發送資料),然後等待足夠長的時間(2MSL)以確保對方接收到ACK包(考慮到丟失ACK包的可能和迷路重複資料包的影響),最後回到CLOSED狀態,釋放網路資源。
- CLOSE_WAIT:表示被動關閉串連的一方在等待關閉串連。當收到對方發送的FIN包後(表示對方不再有資料需要發送),相應的返回ACK包,然後進入CLOSE_WAIT狀態。在該狀態下,若己方還有資料未發送,則可以繼續向對方進行發送,但不能再讀取資料,直到資料發送完畢。
- LAST_ACK:被動關閉串連的一方在CLOSE_WAIT狀態下完成資料的發送後便可向對方發送FIN包(表示己方不再有資料需要發送),然後等待對方返回ACK包。收到ACK包後便回到CLOSED狀態,釋放網路資源。
- CLOSING:比較罕見的例外狀態。正常情況下,發送FIN包後應該先收到(或同時收到)對方的ACK包,再收到對方的FIN包,而CLOSING狀態表示發送FIN包後並沒有收到對方的ACK包,卻已收到了對方的FIN包。有兩種情況可能導致這種狀態:其一,如果雙方几乎在同時關閉串連,那麼就可能出現雙方同時發送FIN包的情況;其二,如果ACK包丟失而對方的FIN包很快發出,也會出現FIN先於ACK到達。
TCP串連的狀態轉換如所示
2. TCP串連的關閉方式
建立TCP串連需要三向交握,而關閉串連則需要四次握手,並且分為主動關閉和被動關閉。這是由於TCP串連是全雙工系統的,我關了你的串連,並不等於你關了我的串連,因此雙方都必須單獨進行關閉。當一方完成它的資料發送任務後可以發送FIN包來終止這個方向的串連,表明自己不再有資料需要發送;收到FIN包的那一方雖然不能再讀取資料,但仍能發送資料。以Client主動關閉串連為例:
- Client向Server發送FIN包,表示Client主動關閉串連,然後進入FIN_WAIT_1狀態,等待Server返回ACK包。此後Client不能再向Server發送資料,但能讀取資料。
- Server收到FIN包後向Client發送ACK包,然後進入CLOSE_WAIT狀態,此後Server不能再讀取資料,但可以繼續向Client發送資料。Client收到Server返回的ACK包後進入FIN_WAIT_2狀態,等待Server發送FIN包。
- Server完成資料的發送後,將FIN包發送給Client,然後進入LAST_ACK狀態,等待Client返回ACK包,此後Server既不能讀取資料,也不能發送資料。
- Client收到FIN包後向Server發送ACK包,然後進入TIME_WAIT狀態,接著等待足夠長的時間(2MSL)以確保Server接收到ACK包,最後回到CLOSED狀態,釋放網路資源。Server收到Client返回的ACK包後便回到CLOSED狀態,釋放網路資源。
TCP串連的建立到關閉,需要經曆以下狀態遷移(假定Client發起串連,並主動關閉串連):
CLOSED -> SYN_SENT -> ESTABLISHED -> FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED
CLODES -> LISTEN -> SYN_RECEIVED -> ESTABLISHED -> CLOSE_WAIT -> LAST_ACK -> CLOSED
3. 對Server與Client的影響
在詳細瞭解TCP串連的狀態和關閉方式後,我們會發現TIME_WAIT狀態是一個坑爹的存在!主動關閉串連的一方在發送最後一個ACK包後,無論對方是否收到都會進入TIME_WAIT狀態,等待2MSL的時間,然後才能釋放網路資源。MSL就是Maximum Segment Lifetime(資料包的最大生命週期),是一個資料包能在互連網上生存的最長時間,若超過這個時間則該資料包將會消失在網路中。作業系統通常會將2MSL設為4分鐘,最低不少於30秒,因而TIME_WAIT狀態一般維持在30秒至4分鐘。這個是TCP/IP協議必不可少的,是TCP/IP設計者設計的,也就是無法解決的。TIME_WAIT狀態的存在主要有兩個原因:
- 可靠地實現TCP全雙工系統串連的終止。在關TCP閉串連時,最後的ACK包是由主動關閉方發出的,如果這個ACK包丟失,則被動關閉方將重發FIN包,因此主動方必須維護狀態資訊,以允許它重發這個ACK包。如果不維持這個狀態資訊,那麼主動方將回到CLOSED狀態,並對被動方重發的FIN包響應RST包,而被動關閉方將此包解釋成一個錯誤(在Java中會拋出connection reset的SocketException)。因而,要實現TCP全雙工系統串連的正常終止,必須能夠處理四次握手協議中任意一個包丟失的情況,主動關閉方必須維持狀態資訊進入TIME_WAIT狀態。
- 確保迷路重複資料包在網路中消失,防止上一次串連中的包迷路後重新出現,影響新串連。TCP資料包可能由於路由器異常而迷路,在迷路期間,資料包發送方可能因逾時而重發這個包,迷路的資料包在路由器恢複後也會被送到目的地,這個迷路的資料包就稱為Lost Duplicate。在關閉一個TCP串連後,如果馬上使用相同的IP地址和連接埠建立新的TCP串連,那麼有可能出現前一個串連的迷路重複資料包在前一個串連關閉後再次出現,影響建立立的串連。為了避免這一情況,TCP協議不允許使用處於TIME_WAIT狀態的串連的IP和連接埠啟動一個新串連,只有經過2MSL的時間,確保上一次串連中所有的迷路重複資料包都已消失在網路中,才能安全地建立新串連。
對於Client而言,每個串連都需要佔用一個連接埠,而系統允許的可用連接埠數不足65000個(這也是在TCP參數最佳化後才能達到)。因此,如果Client發起過多的串連並主動關閉(假設沒有重用連接埠或者串連多個Server),就會有大量的串連在關閉後處於TIME_WAIT狀態,等待2MSL的時間後才能釋放網路資源(包括連接埠),於是Client會由於缺少可用連接埠而無法建立串連。
對Server而言(特別是處理高並發短串連的Server),Server端與Client建立的串連是使用同一個連接埠的,即監聽的連接埠,每個串連通過一個五元組區分,包括源IP地址、源連接埠、傳輸層協議號(協議類型)、目的IP地址、目的連接埠,因而在理論上,Server不受系統連接埠數的限制。但是,Server對每個連接埠上的串連數是有限制的,它要使用雜湊表記錄連接埠上的每個串連,並受到檔案描述符的最大開啟數的限制。所以,如果Server主動關閉串連,同樣會有大量的串連在關閉後處於TIME_WAIT狀態,等待2MSL的時間後才能釋放網路資源(包括雜湊表上的串連記錄和檔案描述符),於是Server會由於達到雜湊表和檔案描述符的限制而無法接受新串連,造成效能的急劇下滑,效能曲線會持續產生嚴重的波動。對於這種情況,有三種應對方式:
- 試圖讓Client主動關閉串連,由於每個Client的並發量都比較低,因而不會產生效能瓶頸。
- 最佳化Server的系統TCP參數,使其網路資源的最大值、消耗速度和恢複速度達到平衡。
- 改寫TCP協議,重新實現底層代碼,不過該方式難度很大,而且系統的穩定性和安全性可能受到影響。
4. TCPWindowSize
TCPWindowSize的值表示TCP的視窗大小。TCP Receive Window(TCP資料接收緩衝)定義了發送端在沒有獲得接收端的確認資訊的狀態下可以發送的最大位元組數。此數值越大,返回的確認資訊就越少,相應的在發送端和接收端之間的通訊就越好。此數值較小時可以降低發送端在等待接收端返回確認資訊時發生逾時的可能性,但這將增加網路流量,降低有效吞吐率。TCP在發送端和接收端之間動態調整一個最大段長度MSS(Maximum Segment Size)的整數倍。MSS在串連開始建立時確定,由於TCP Receive Window被調整為MSS的整數倍,在資料轉送中完全長度的TCP資料區段的比例增加,故而提高了網路吞吐率。
預設情況下,TCP將試圖根據MSS來最佳化視窗大小,起始值為16KB,最大值為64KB。TCPWindowSize的最大值通常為65535位元組(64KB),乙太網路最大段長度為1460位元組,低於64KB的1460的最大整數倍為62420位元組,因而可以在註冊表中將TCPWindowSize設定為62420,作為高頻寬網路中適用的效能最佳化值。具體操作如下:
瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters註冊表子鍵,在Parameters子鍵下建立或修改名為TCPWindowSize的REG_DWORD值,該值的範圍是從0到65535,將該值設定為62420。
5. TCP1323Opts
為了更高效地利用高頻寬網路,可以使用比上述TCP視窗大得多的TCP視窗大小,此特性是Windows 2000和Windows Server 2003中的新特性,稱為TCP Window Scaling,它將以前的65535位元組(64KB)的限制提高到了1073741824位元組(1GB)。在頻寬與延遲的乘積值很高的串連上(例如衛星串連),可能需要將視窗的大小增加到64KB以上。使用TCP Window Scaling,系統可以允許確認資訊間更大資料量的傳輸,增加了網路輸送量及效能。發送端和接收端往返通訊所需的時間被稱為迴環時間(RTT)。TCP Window Scaling僅在TCP串連的雙方都開啟時才真正有效。TCP有一個時間戳記選項,通過更加頻繁地計算來提高RTT值的估測值,此選項特別有助於估測更長距離的廣域網路上串連的RTT值,並更加精確地調整TCP重發逾時時間。時間戳記在TCP前序提供了兩個地區,一個記錄開始重發的時間,另一個記錄接收到的時間。時間戳記對於TCP Window Scaling,即確認資訊收到前的大資料包傳送特別有用,啟用時間戳記僅僅在每個資料包的頭部增加12位元組,對網路流量的影響微乎其微。資料完整性與資料吞吐率最大化哪個更為重要是個需要評估的問題。在某些環境中,例如視頻流傳輸,需要更大的TCP視窗,這是最重要的,而資料完整性排在第二位。在這種環境中,TCP Window Scaling可以不開啟時間戳記。當發送端和接收端均啟用TCP Window Scaling和時間戳記時,此特性才有效。不過,若在發包時加入了時間戳記,經過NAT之後,如果前面相同的連接埠被使用過,且時間戳記大於這個串連發出的SYN中的時間戳記,就會導致伺服器忽略該SYN,表現為使用者無法正常完成TCP的3次握手。初始時產生小的TCP視窗,之後視窗大小將按照內部演算法增大。具體操作如下:
瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters註冊表子鍵,在Parameters子鍵下建立或修改名為TCP1323Opts的REG_DWORD值,該值的具體含義為:0(預設值)表示禁用TCP Window Scaling和時間戳記;1表示只啟用TCP Window Scaling;2表示只啟用時間戳記;3表示同時啟用TCP Window Scaling和時間戳記。TCP1323Opts設定為啟用TCP Window Scaling後,可以將上文中的登錄機碼TCPWindowSize的值增大,最大能達到1GB,為了達到最佳效能,這裡的值最好設定成MSS的倍數,推薦值為256960位元組。
6. TCP 控制塊表
對於每個TCP串連,控制變數儲存在一個稱為TCP控制塊(TCB)的記憶體塊中。TCB表的大小由登錄機碼MaxHashTableSize控制。在活動串連很多的系統中,設定一個較大的表可以降低系統定位TCB表的時間。在TCB表上分區可以降低對錶的訪問的爭奪。增加分區的數量,TCP的效能會得到最佳化,特別是在多處理器的系統上。登錄機碼NumTcbTablePartitions控制分區的數量,預設是處理器個數的平方。TCB通常預置在記憶體中,以防止TCP反覆串連和斷開時,TCB反覆重新置放浪費時間,這種緩衝的方式促進了記憶體管理,但同時也限制了同一時刻允許的TCP串連數量。登錄機碼MaxFreeTcbs決定了處於空閑等待狀態的TCB重新可用之前的串連數量,在NT架構中常設定成高於預設值,以確保有足夠的預置的TCB。從Windows 2000開始添加了一個新特性,降低超出預置TCB啟動並執行可能性。如果處於等待狀態的串連多於MaxFreeTWTcbs中的設定,所有等待時間超過60秒的串連將被強制關閉,以後再次啟用。此特性合并到Windows 2000 Server和Windows Server 2003後,MaxFreeTcbs將不再用於最佳化效能。具體操作:
瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters註冊表子鍵,在Parameters子鍵下建立或修改名為MaxHashTableSize的REG_DWORD值,該值的範圍是從1到65536,並且必須為2的N次方,預設值為512,建議設為8192。然後在Parameters子鍵下建立或修改名為NumTcbTablePartitions的REG_DWORD值,該值的範圍是從1到65536,並且必須為2的N次方,預設值為處理器個數的平方,建議設為處理器核心數的4倍。
7. TcpTimedWaitDelay
TcpTimedWaitDelay的值表示系統釋放已關閉的TCP串連並複用其資源之前,必須等待的時間。這段時間間隔就是以前的Blog中提到的TIME_WAIT狀態(2MSL,資料包最長生命週期的兩倍狀態)。如果系統顯示大量串連處於TIME_WAIT狀態,則會導致並發量與輸送量的嚴重下降,通過減小該項的值,系統可以更快地釋放已關閉的串連,從而為新串連提供更多的資源,特別是對於高並發短串連的Server具有積極的意義。
該項的預設值是240,即等待4分鐘後釋放資源;系統支援的最小值為30,即等待時間為30秒。具體操作:
瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters註冊表子鍵,在Parameters子鍵下建立或修改名為TcpTimedWaitDelay的REG_DWORD值,該值的範圍是從0到300,建議將該值設定為30。
8. MaxUserPort
MaxUserPort的值表示當應用程式向系統請求可用的連接埠時,TCP/IP可分配的最大連接埠號碼。如果系統顯示建立串連時出現異常,那麼有可能是由於匿名(臨時)連接埠數不夠導致的,特別是當系統開啟大量連接埠來與Web service、資料庫或其他遠端資源建立串連時。
該項的預設值是十進位的5000,這也是系統允許的最小值。Windows預設為匿名(臨時)連接埠保留的連接埠號碼範圍是從1024到5000。為了獲得更高的並發量,建議將該值至少設為32768以上,甚至設為理論最大值65534,特別是對於類比高並發測試環境的Client具有積極的意義。具體操作:
瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters註冊表子鍵,在Parameters子鍵下建立或修改名為MaxUserPort的REG_DWORD值,該值的範圍是從5000到65534,預設值為5000,建議將該值設定為65534。
9. 動態儲備
動態儲備的值使系統能自動調整其配置,以接受大量突發的串連請求。如果同時接收到大量串連請求,超出了系統的處理能力,那麼動態儲備就會自動增大系統支援的暫掛串連的數量(即Client已請求而Server尚未處理的等待串連數,TCP串連的總數包括已串連數與等待串連數),從而可減少串連失敗的數量。系統的處理能力和支援的暫掛串連的數量不足時,Client的串連請求將直接被拒絕。
預設情況下,Windows 不啟用動態儲備,可以通過以下操作進行開啟和設定:
瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters註冊表子鍵,在Parameters子鍵下建立或修改下列名稱的REG_DWORD值。
- EnableDynamicBacklog,值為1,表示開啟動態儲備。
- MinimumDynamicBacklog,值為128,表示支援的最小暫掛串連的數量為128。
- MaximumDynamicBacklog,值為2048,表示支援的最大暫掛串連的數量為2048。對於高並發短串連的Server,建議最大值設為1024及以上。
- DynamicBacklogGrowthDelta,值為128,表示支援的暫掛串連的數量的增量為128,即數量不足時自增長128,直到達到設定的最大值,如2048。
10. KeepAliveTime
KeepAliveTime的值控制系統嘗實驗證空閑串連是否仍然完好的頻率。如果該串連在一段時間內沒有活動,那麼系統會發送保持串連的訊號,如果網路正常並且接收方是活動的,它就會響應。如果需要對丟失接收方的情況敏感,也就是說需要更快地發現是否丟失了接收方,請考慮減小該值。而如果長期不活動的空閑串連的出現次數較多,但丟失接收方的情況出現較少,那麼可能需要增大該值以減少開銷。
預設情況下,如果空閑串連在7200000毫秒(2小時)內沒有活動,系統就會發送保持串連的訊息。 通常建議把該值設為1800000毫秒,從而丟失的串連會在30分鐘內被檢測到。具體操作:
瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters註冊表子鍵,在Parameters子鍵下建立或修改名為KeepAliveTime的REG_DWORD值,為該值設定適當的毫秒數。
11. KeepAliveInterval
KeepAliveInterval的值表示未收到另一方對“保持串連”訊號的響應時,系統重複發送“保持串連”訊號的頻率。在無任何響應的情況下,連續發送“保持串連”訊號的次數超過TcpMaxDataRetransmissions(下文將介紹)的值時,將放棄該串連。如果網路環境較差,允許較長的回應時間,則考慮增大該值以減少開銷;如果需要儘快驗證是否已丟失接收方,則考慮減小該值或TcpMaxDataRetransmissions值。
預設情況下,在未收到響應而重新發送“保持串連”的訊號之前,系統會等待1000毫秒(1秒),可以根據具體需求修改,具體操作:
瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters註冊表子鍵,在Parameters子鍵下建立或修改名為KeepAliveInterval的REG_DWORD值,為該值設定適當的毫秒數。
12. TcpMaxDataRetransmissions
TcpMaxDataRetransmissions的值表示TCP資料重發,系統在現有串連上對無應答的資料區段進行重發的次數。如果網路環境很差,可能需要提高該值以保持有效通訊,確保接收方收到資料;如果網路環境很好,或者通常是由於丟失接收方而導致資料的丟失,那麼可以減小該值以減少驗證接收方是否丟失所花費的時間和開銷。
預設情況下,系統會重新發送未返回應答的資料區段5次,可以根據具體需求修改,具體操作:
瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters註冊表子鍵,在Parameters子鍵下建立或修改名為TcpMaxDataRetransmissions的REG_DWORD值,該值的範圍是從0到4294967295,預設值為5,根據實際情況進行設定。
13. TcpMaxConnectRetransmisstions
TcpMaxConnectRetransmisstions的值表示TCP串連重發,TCP退出前重發非確認串連請求(SYN)的次數。對於每次嘗試,重發逾時是成功重發的兩倍。在Windows Server 2003中預設逾時次數是2,預設逾時時間為3秒(在登錄機碼TCPInitialRTT中)。速度較慢的WAN串連中逾時時間可相應增加,不同環境中可能會有不同的最佳化設定,需要在實際環境中測試確定。逾時時間不要設定太大否則將不會發生網路連接逾時時間。具體操作:
瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters註冊表子鍵,在Parameters子鍵下建立或修改名為TcpMaxConnectRetransmisstions的REG_DWORD值,該值的範圍是從0到255,預設值為2,根據實際情況進行設定。然後在Parameters子鍵下建立或修改名為TCPInitialRTT的REG_DWORD值,同樣根據實際情況進行設定。
14. TcpAckFrequency
TcpAckFrequency的值表示系統發送應答訊息的頻率。如果值為2,那麼系統將在接收到2個分段之後發送應答,或是在接收到1個分段但在200毫秒內沒有接收到任何其他分段的情況下發送應答;如果值為3,那麼系統將在接收到3個分段之後發送應答,或是在接收到1個或2個分段但在200毫秒內沒有接收到任何其他分段的情況下發送應答,以此類推。如果要通過消除應答延遲來縮短回應時間,那麼建議將該值設為1。在此情況下,系統會立即發送對每個分段的應答;如果串連主要用於傳輸大量資料,而200毫秒的延遲並不重要,那麼可以減小該值以降低應答的開銷。
預設情況下,系統將該值設為2,即每隔一個分段應答一次。該值的有效範圍是0到255,其中0表示使用預設值2,可以根據具體需求修改,具體操作:
瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters\Interfaces\xx(xx由網路介面卡決定)註冊表子鍵,在xx子鍵下建立或修改名為TcpAckFrequency的REG_DWORD值,該值的範圍是從1到13,預設值為2,根據希望每發送幾個分段返回一個應答而設定該值,建議百兆網路設為5,千兆網路設為13。
Windows系統下的TCP參數最佳化