前面兩篇文章介紹了TCP狀態變遷,以及經過實驗演示了客戶端和服務端的正常狀態變遷。html
下面就來看看TCP狀態變遷過程當中的幾個特殊狀態。瀏覽器
在TCP鏈接創建的過程當中,當服務端接收到[SYN]包後,就會發送[SYN, ACK]包,而後進入SYN_RCVD狀態。服務器
根據前面文章的介紹,服務器的上述行爲被稱爲被動打開,而且會等待來自客戶的的[ACK]包來完成TCP鏈接的創建。可是,若是此時客戶端沒有響應,服務端就會超時重傳[SYN, ACK]包。併發
回想一下咱們在"動手學習TCP: 環境搭建"一文中使用的例子,這個例子就只是客戶端向服務端發送一個TCP鏈接創建請求包,而後就進入等待狀態了。socket
讓咱們再次運行這個例子,經過Wireshark抓包能夠看到,虛擬機中的服務端進行了五次超時重傳,間隔爲3s,6s,12s,24s,一共45s;可是,當第五個[SYN, ACK]包發送後,服務器將會繼續等待48s,最終第五次重傳也超時了。學習
在服務器重傳這段時間,經過虛擬機中的命令行運行 netstat -anp TCP | findstr "192.168.56" 命令,會看到服務器處於SYN_RCVD狀態。ui
從上面的實驗結果能夠看到,當服務端收到客戶端的TCP鏈接請求後,會發送[SYN, ACK]包,進入SYN_RCVD狀態。若是沒有收到客戶端的確認,服務器會嘗試重傳,並保持SYN_RCVD狀態一段時間(一般是30秒到2分鐘)。spa
因爲服務端的SYN_RCVD狀態,就有了SYN Flood攻擊。操作系統
所謂的SYN Flood攻擊就是,惡意的客戶端給服務端發了一個SYN後,就下線了,因而服務器須要默認等93s(一般是30秒到2分鐘,上面的例子是93s)纔會斷開鏈接。命令行
這樣,攻擊者就能夠把服務器的SYN鏈接的隊列耗盡,讓正常的鏈接請求不能處理。
對於如何避免SYN Flood攻擊,服務端有不少設置方式,這裏就不介紹了,有興趣能夠網上查查。
在客戶端的正常狀態變遷中,客戶端主動終止TCP鏈接,而後就會從TIME_WAIT狀態到CLOSED狀態。
TIME_WAIT狀態也稱爲2MSL(Maximum Segment Lifetime)等待狀態,這個設置是TCP中4中定時器之一(另外的3個定時器後面介紹)。
RFC793定義了MSL爲2分鐘,可是在實現中,MSL通常爲30秒,1分鐘或者兩分鐘。
之因此有一個TIME_WAIT狀態,而不是直接轉換成CLOSED狀態,主要有下面兩個緣由:
當一端進入TIME_WAIT狀態後,所產生的效果就是該端口在2MSL這段時間中不能被再次使用。
看一個實驗例子,因爲 操做系統不能檢測到Pcap.Net實現的客戶端的TCP鏈接狀態,因此經過Python實現了一個簡單的socket客戶端,並強制指定客戶端的端口號爲3333:
from socket import * import time Client_ADDR = ("192.168.56.101", 3333) Server_ADDR = ("192.168.56.102", 8081) BUFSIZ = 1024 client = socket(AF_INET, SOCK_STREAM) client.bind(Client_ADDR) client.connect(Server_ADDR) print "client connect to server" print "quit after 5 seconds" time.sleep(5) client.close()
當程序運行後,能夠經過netstat命令看到客戶端顯示進入"ESTABLISHED"狀態,當終止鏈接後,就進入了"TIME_WAIT"狀態。
這時,當再次運行客戶端程序的時候,就會遇到下面的異常,提示端口被佔用:
從上面的介紹能夠看到,主動終止TCP鏈接的一端會進入TIME_WAIT狀態,該端TCP鏈接的端口將在2MSL時間中不可用。
若是在大併發的短鏈接狀況下,TIME_WAIT 就會不少,系統的可用端口資源就會面臨耗盡的狀況。
這也就說明了HTTP的KeepAlive對HTTP服務器是多麼的重要,在設置KeepAlive的狀況下,瀏覽器會重用一個TCP鏈接來處理多個HTTP請求,減緩TIME_WAIT帶來的影響。
關於復位報文段,它不是一個TCP狀態,可是確實TCP狀態變遷中不可少的一部分,因此在這裏進行簡單的介紹。
所謂復位報文段就是TCP首部中,設置RST標誌的TCP包。通常來講,不管什麼時候一個報文段發送過程當中遇到鏈接錯誤,TCP都會發出一個[RST]包來重置該TCP鏈接。
通常下面狀況下會常常碰到[RST]包:
此次依然運行"動手學習TCP: 環境搭建"中的例子,只是把目標端口改成"1234"。
EndPointInfo endPointInfo = new EndPointInfo(); endPointInfo.SourceMac = "08:00:27:00:C0:D5"; endPointInfo.DestinationMac = "08:00:27:70:A6:AE"; endPointInfo.SourceIp = "192.168.56.101"; endPointInfo.DestinationIp = "192.168.56.102"; endPointInfo.SourcePort = 3330; //endPointInfo.DestinationPort = 8081; endPointInfo.DestinationPort = 1234;
運行程序,因爲虛擬機中的"1234"端口並非一個TCP監聽端口,因此就會收到來自虛擬機的[RST]包:
前面已經看到,正常終止一個TCP鏈接須要進行四次揮手,這也被稱爲有序釋放(orderly release)。
可是,也有狀況是經過[RST]包來釋放一個鏈接,這種狀況被稱爲異常釋放(abortive release)。
異常終止一個鏈接對應用程序來講有兩個優勢:
本文介紹了TCP狀態轉換中的兩個特殊狀態:SYN_RCVD和TIME_WAIT。
SYN_RCVD狀態會使服務端的特定端口,在一段時間內重傳[SYN, ACK]包,直到超時或者客戶端有相應;在該端時間內,服務器的該端口被佔用。TIME_WAIT狀態則是,主動關閉TCP鏈接的一端,會保持2MSL的時間後,才進入CLOSED狀態。
後半部分簡單介紹了復位報文段,以及復位報文段常用的狀況。