TCP四次揮手:
Time_wait產生原因及作用:
1. time_wait狀態如何產生?
由上面的變遷圖,首先調用close()發起主動關閉的一方,在發送最后一個ACK之后會進入time_wait的狀態,也就說該發送方會保持2MSL時間之后才會回到初始狀態。MSL值得是數據包在網絡中的最大生存時間。產生這種結果使得這個TCP連接在2MSL連接等待期間,定義這個連接的四元組(客戶端IP地址和端口,服務端IP地址和端口號)不能被使用。
2.time_wait狀態產生的原因
1)為實現TCP全雙工連接的可靠釋放
由TCP狀態變遷圖可知,假設發起主動關閉的一方(client)最后發送的ACK在網絡中丟失,由於TCP協議的重傳機制,執行被動關閉的一方(server)將會重發其FIN,在該FIN到達client之前,client必須維護這條連接狀態,也就說這條TCP連接所對應的資源(client方的local_ip,local_port)不能被立即釋放或重新分配,直到另一方重發的FIN達到之后,client重發ACK后,經過2MSL時間周期沒有再收到另一方的FIN之后,該TCP連接才能恢復初始的CLOSED狀態。如果主動關閉一方不維護這樣一個TIME_WAIT狀態,那么當被動關閉一方重發的FIN到達時,主動關閉一方的TCP傳輸層會用RST包響應對方,這會被對方認為是有錯誤發生,然而這事實上只是正常的關閉連接過程,並非異常。
確保被動關閉方收到ACK,連接正常關閉,且不因被動關閉方重傳FIN影響下一個新連接
2)為使舊的數據包在網絡因過期而消失
為說明這個問題,我們先假設TCP協議中不存在TIME_WAIT狀態的限制,再假設當前有一條TCP連接:(local_ip, local_port, remote_ip,remote_port),因某些原因,我們先關閉,接着很快以相同的四元組建立一條新連接。本文前面介紹過,TCP連接由四元組唯一標識,因此,在我們假設的情況中,TCP協議棧是無法區分前后兩條TCP連接的不同的,在它看來,這根本就是同一條連接,中間先釋放再建立的過程對其來說是“感知”不到的。這樣就可能發生這樣的情況:前一條TCP連接由local peer發送的數據到達remote peer后,會被該remot peer的TCP傳輸層當做當前TCP連接的正常數據接收並向上傳遞至應用層(而事實上,在我們假設的場景下,這些舊數據到達remote peer前,舊連接已斷開且一條由相同四元組構成的新TCP連接已建立,因此,這些舊數據是不應該被向上傳遞至應用層的),從而引起數據錯亂進而導致各種無法預知的詭異現象。作為一種可靠的傳輸協議,TCP必須在協議層面考慮並避免這種情況的發生,這正是TIME_WAIT狀態存在的第2個原因。
2MSL:報文最大生存時間,確保舊的數據不會影響新連接
3)總結
具體而言,local peer主動調用close后,此時的TCP連接進入TIME_WAIT狀態,處於該狀態下的TCP連接不能立即以同樣的四元組建立新連接,即發起active close的那方占用的local port在TIME_WAIT期間不能再被重新分配。由於TIME_WAIT狀態持續時間為2MSL,這樣保證了舊TCP連接雙工鏈路中的舊數據包均因過期(超過MSL)而消失,此后,就可以用相同的四元組建立一條新連接而不會發生前后兩次連接數據錯亂的情況。
快速回收Time_wait:
方法:
- vi /etc/sysctl.conf
- 編輯文件,加入以下內容:
net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 30
3.然后執行/sbin/sysctl -p讓參數生效。
net.ipv4.tcp_syncookies = 1表示開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防范少量SYN攻擊,默認為0,表示關閉;
net.ipv4.tcp_tw_reuse = 1表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連接,默認為0,表示關閉;
net.ipv4.tcp_tw_recycle = 1表示開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0,表示關閉。
net.ipv4.tcp_fin_timeout 修改系統默認的TIMEOUT時間
修改之后,再用命令查看TIME_WAIT連接數netstat -ant |grep “TIME_WAIT” |wc –l。在沒有nat情況下還需要設置net.ipv4.tcp_timestamps = 1才能生效。關於tcp_tw_recycle參數,TCP有一種行為,可以緩存每個連接最新的時間戳,后續請求中如果時間戳小於緩存的時間戳,即視為無效,相應的數據包會被丟棄。Linux是否啟用這種行為取決於tcp_timestamps和tcp_tw_recycle,因為tcp_timestamps缺省就是開啟的,所以當tcp_tw_recycle被開啟后,實際上這種行為就被激活了。在nat環境中會出現時間戳錯亂的情況,后面的數據包就被丟棄了,具體的 表現通常是是客戶端明明發送的SYN,但服務端就是不響應ACK。因為NAT設備將數據包的源IP地址都改成了一個地址(或者少量的IP地址),但是卻基本上不修改TCP包的時間戳,則會導致時間戳混亂。建議:如果前端部署了三/四層NAT設備,盡量關閉快速回收,以免發生NAT背后真實機器由於時間戳混亂導致的SYN拒絕問題。
重用Time_wait:
net.ipv4.tcp_tw_reuse = 1
如果能保證以下任意一點,一個TW狀態的四元組(即一個socket連接)可以重新被新到來的SYN連接使用:
1.初始序列號比TW老連接的末序列號大
2.如果使能了時間戳,那么新到來的連接的時間戳比老連接的時間戳大