動手學習TCP:TCP特殊狀態

前面兩篇文章介紹了TCP狀態變遷,以及經過實驗演示了客戶端和服務端的正常狀態變遷。html

下面就來看看TCP狀態變遷過程當中的幾個特殊狀態。瀏覽器

SYN_RCVD

在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

SYN Flood攻擊

從上面的實驗結果能夠看到,當服務端收到客戶端的TCP鏈接請求後,會發送[SYN, ACK]包,進入SYN_RCVD狀態。若是沒有收到客戶端的確認,服務器會嘗試重傳,並保持SYN_RCVD狀態一段時間(一般是30秒到2分鐘)。spa

因爲服務端的SYN_RCVD狀態,就有了SYN Flood攻擊。操作系統

所謂的SYN Flood攻擊就是,惡意的客戶端給服務端發了一個SYN後,就下線了,因而服務器須要默認等93s(一般是30秒到2分鐘,上面的例子是93s)纔會斷開鏈接。命令行

這樣,攻擊者就能夠把服務器的SYN鏈接的隊列耗盡,讓正常的鏈接請求不能處理。

對於如何避免SYN Flood攻擊,服務端有不少設置方式,這裏就不介紹了,有興趣能夠網上查查。

TIME_WAIT

在客戶端的正常狀態變遷中,客戶端主動終止TCP鏈接,而後就會從TIME_WAIT狀態到CLOSED狀態。

TIME_WAIT狀態也稱爲2MSL(Maximum Segment Lifetime)等待狀態,這個設置是TCP中4中定時器之一(另外的3個定時器後面介紹)。

RFC793定義了MSL爲2分鐘,可是在實現中,MSL通常爲30秒,1分鐘或者兩分鐘。

爲何有 TIME_WAIT

之因此有一個TIME_WAIT狀態,而不是直接轉換成CLOSED狀態,主要有下面兩個緣由:

  1. 客戶端發送最後的確認[ACK]後進入TIME_WAIT狀態,可是這個[ACK]包可能會丟失;這種狀況下服務端會重傳[FIN, ACK]。也就是說,TIME_WAIT停留2被的MSL就是爲了讓TCP再次發送最後的[ACK]以方式這個[ACK]丟失。
  2. 防止上一次鏈接中的包,迷路後從新出現,影響新鏈接(通過2MSL,上一次鏈接中全部的重複包都會消失)

TIME_WAIT的效果

當一端進入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"狀態。

這時,當再次運行客戶端程序的時候,就會遇到下面的異常,提示端口被佔用:

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)。

異常終止一個鏈接對應用程序來講有兩個優勢:

  1. 丟棄任何帶發送數據並當即發送復位報文段
  2. [RST]包的接收方可以區分另外一端執行的是異常關閉仍是正常關閉。

總結

本文介紹了TCP狀態轉換中的兩個特殊狀態:SYN_RCVD和TIME_WAIT。

SYN_RCVD狀態會使服務端的特定端口,在一段時間內重傳[SYN, ACK]包,直到超時或者客戶端有相應;在該端時間內,服務器的該端口被佔用。TIME_WAIT狀態則是,主動關閉TCP鏈接的一端,會保持2MSL的時間後,才進入CLOSED狀態。

後半部分簡單介紹了復位報文段,以及復位報文段常用的狀況。

相關文章
相關標籤/搜索