1. 程式人生 > >TCP協議詳解之TCP Flag標誌位來判斷TCP會話的開始和結束

TCP協議詳解之TCP Flag標誌位來判斷TCP會話的開始和結束

首先回顧一下TCP標誌位的具體含義。

TCP Flag標誌位(控制位)

一個TCP包的詳細內容:

TCP FLAG 標記佔1.5個byte,12bit(4bit+8bit,前半個byte與Header Length公用)。

12bit中前三個bit是保留,預設為全0:

000. .... .... = Reserved: Not set

第4個bit為:

...0 .... .... = Nonce: Not set

第5個bit為:

.... 0... .... = Congestion Window Reduced (CWR): Not set

第6個bit為:

.... .0.. .... = ECN-Echo: Not set

從第7個bit到第12個bit之間的六個bit是我們常說的6個標誌位分別是:

.... ..0. .... = Urgent: Not set

.... ...1 .... = Acknowledgment: Set

.... .... 0... = Push: Not set

.... .... .0.. = Reset: Not set

.... .... ..0. = Syn: Not set

.... .... ...1 = Fin: Set

TCP標記的具體意義如下所列:

W : CWR(Congestion Window Reduced) - 擁塞視窗減少。CWR標誌與後面的ECE標誌都用於IP首部的ECN欄位。ECE標誌為1時,則通知對方已將擁塞視窗縮小。

E : ECE(ECN-Echo) - 顯式擁塞提醒迴應。置為1會通知通訊對方,從對方到這邊的網路有擁塞。在收到資料包的IP首部中ECN為1時,將TCP首部中的ECE設定為1.

U : URG - 緊急指標,表明傳送端向另一端使用緊急方式傳送資料。 包中有需要緊急處理的資料。

A : ACK - 應答響應,表示確認序號有效。確認應答的欄位有效。TCP規定除了最初建立連線時的SYN包之外該位必須設定為1。

P : PUSH - 推送; 資料包立即傳送 。表示接收方應該儘快將這個報文交給應用層。表示需要將收到的資料立刻傳給上層應用協議。PSH為0,也就是普通情況下,則不需要立即傳,而是先進行快取。

R : RST - 復位;中斷一個連線,表示連線重置。​​​​​​​ 表示TCP連線中出現異常必須強制斷開連線。例如,一個沒有被使用的埠即使發來了連線請求,也無法通訊。此時就可以返回一個RST設定為1的包。此外,程式宕掉或切斷電源等原因導致主機重啟的情況下,由於所有的連線資訊將全部被初始化,所以原有的TCP通訊也將不能繼續進行。這種情況下,如果通訊對方傳送了一個設定為1的RST包,就會使通訊強制被斷開。

S : SYN - 同步; 表示開始會話請求,用來發起一個連線,建立連線。SYN為1表示希望建立連線,並在其序列號的欄位進行序列號初始值的設定。(Synchronize本身有同步的意思。也就是意味著建立連線的雙方,序列號和確認應答號要保持同步) 

F : FIN - 結束; 結束會話,關閉連線。表示傳送方完成任務,今後不會有資料傳送,希望斷開連線。當通訊結束希望斷開連線,通訊雙方的主機之間就可以相互交換FIN位置為1的TCP段。每個主機又對對方的FIN包進行確認應答以後就可以斷開連線。不過,主機收到FIN設定為1的TCP段以後不必馬上回復一個FIN包,而是可以等到緩衝區中的所有資料都已成功傳送而被自動刪除之後再發FIN包
因此,02表明該資料包傳送的是一個同步序號,用來發起一個新的連線。Flags: 0x002 (SYN)

注意:URG和PUSH的區別是:

當URG = 1時表明緊急指標欄位有效,告訴系統此報文段中有緊急資料,應儘快傳送,而不要按原來的排隊順序來傳送(進入快取,等快取區滿再提交給應用),傳送方的TCP就把緊急資料放到本報文段資料的最前面(因為緊急指標從頭開始算)。URG標誌位要與首部中的緊急指標欄位配合使用,緊急指標指向資料段中的某個位元組,(資料從第一個位元組到指標所指的位元組就是緊急資料,若URG為0,則緊急指標沒有意義、)。值得注意的是即使視窗為0時也可以傳送緊急資料,緊急資料不進入接收緩衝區直接交給上層程序(本來在緩衝區中的資料無影響)。

當傳送端將PSH置為1時,TCP會立即建立一個報文併發送。接受端收到PSH為1的報文後,首先將傳送過來的PSH報文的資料加入到接收緩衝區,然後立即將接收緩衝區內資料向上交付給應用程式,而不是等待緩衝區滿後再交付。當兩個應用程序進行互動式通訊時,有時客戶發一個請求給伺服器時希望立即能夠收到對方的響應,這種情況下,客戶應用程式通知TCP使用推送(push)操作,TCP就把PSH置為1,並立即建立一個報文段傳送過去;伺服器端收到一個設了PSH標誌的報文段時就儘快將所有收到的資料立即提交給服務程序,而不在等到整個快取都填滿了再向上交付。
雖然應用程序可以選擇推送操作,但推送操作還是很少使用。兩者都可理解為處理緊急資料的標誌位,只是處理方法不同。URG的緊急資料僅在報文內,而PSH的緊急資料還在接受緩衝區內。也就是URG不進入快取。

注意:

其他文獻和部落格中關於TCP報文中Flag位看到了幾種不同的版本:
第一種:保留6位,Flag位分別是URG,ACK,PSH,RST,SYN,FIN
第二種:保留4位,Flag位分別是CWR,ECE,URG,ACK,PSH,RST,SYN,FIN(圖解TCP/IP)
第三種:保留3位,Flag位分別是Nonce,CWR,ECE,URG,ACK,PSH,RST,SYN,FIN(Wireshark)

不管怎麼樣後六位一定是標誌位,而且是最重要的。

CWR,ECE是用於傳輸過程中的擁塞控制的,與tcp的視窗協同工作,不過好像極少用的到。

不合法的標誌位搭配

不合法的標誌位組合。

1、所有標誌位都為0。

2、SYN和FIN同時被置1。

3、SYN和RST同時被置1。

4、FIN和RST同時被置1。

5、FIN位被置1,但ACK位沒有被置1。

6、PSH位被置1,但ACK位沒有被置1。

7、URG位被置1,但ACK位沒有被置1。

有幾個欄位與flags有關,在此專門介紹一下:

序列號(Sequence Number)

欄位長32位。序列號(也稱為序號)是指傳送資料的位置。每傳送一次資料,就累加一次該資料位元組數的大小。序列號不會從0或1開始(因為防止被猜解,重放攻擊),而是在建立連線時由計算機生成的隨機數作為其初始值(不同作業系統產生隨機數的機制不太一樣),通過SYN包傳給接收端主機。然後再將每轉發出去的位元組數累加到初始值上表示資料的位置。此外,在建立連線和斷開連線時,傳送的SYN包和FIN包雖然並不攜帶資料,但是也作為一個位元組增加對應的序列號。

確認應答號(Acknowledgement Number)

確認應答號欄位長度32位。指下一次應該收到的資料的序列號。實際上指當前已收到確認應答號減一為止的資料。傳送端收到這個確認應答以後可以認為在這個序號以前的資料都已經被正常接收。

緊急指標

該欄位長為16位。只有在URG控制位為1時有效。該欄位的數值表示本報文段中緊急資料的指標。也就是,從資料部分的首位到緊急指標所指的位置為止為緊急資料。因此也可以說緊急指標指出了緊急資料的末尾在報文段中的位置。一般在暫時中斷通訊的情況下使用。例如在Web瀏覽器點選停止按鈕,或者使用TELNET時輸入Ctrl+C時都會有URG為1的包。此外,緊急指標也可用作表示資料流分段的標誌。


下面對每一個標誌位進行詳解,如果你瞭解了,就不需要看了。

第一個標誌位 - 緊急指標(URGENT POINTER)

第一個標誌是緊急指標標誌,此標誌用於將輸入資料標識為“緊急”。這樣的進入段不必等待直到先前段被接收端消耗,而是直接傳送並立即處理

在資料傳輸流中,主機正在向遠端機器上執行的應用程式傳送資料,可以使用緊急指標。如果出現問題,主機需要中止資料傳輸,並在另一端停止資料處理。在正常情況下,中止訊號將在遠端機器傳送和排隊,直到所有先前傳送的資料都被處理,但是在這種情況下,我們需要立即處理中止訊號。

通過將中止訊號的段緊急指標標誌設定為“1”,遠端機器將不會等待所有排隊的資料被處理,然後執行中止。相反,它會給出特定的段優先順序,立即處理它,並停止進一步的資料處理。

另外要注意的是存在緊急指標欄位。當緊急指標標誌設定為'1'時,緊急指標欄位指定緊急資料結束的段中的位置,無效時預設為0。

第二個標誌位 - 確認(ACKNOWLEDGEMENT)

確認標誌用於確認資料包的成功接收,也用於握手和揮手的確認

如果在使用TCP傳輸資料的同時執行資料包嗅探器,您會注意到,在大多數情況下,對於傳送或接收的每個資料包,都會進行確認。在傳送方每傳送一個數據包需要一個確認,接收端將傳送一次預期的ACK(接收到第三個連續的資料包)。這也稱為視窗化。

TCP連線是可靠的,其可靠性的表現就是即使在傳輸過程中丟包,TCP協議也能自動重傳丟失的包,並保證所有資料包的順序。重傳其實就依賴於ACK。其實在每次傳送資料包後,傳送端會監聽等待接收端傳送一個ACK來表示其已經收到了資料,如果傳送端在等待某個特定時間後(也就是重傳超時)仍然沒有收到接收端的訊息,則傳送端就開始重新傳送那個資料包。所以,ACK並不單純存在於握手中,還存在於整個資料傳輸中。

千萬不要被wireshark騙了,ACK並不是只在建立連線和結束連線時候使用。其實每個tcp請求除了首次建立連線,都是有應答ACK的,不過wireshark有些沒有顯示ACK標誌而已。filter輸入tcp.flags.ack==1,會發現所有的序號都是連續的。(wireshark太智慧會造成一些誤會,比如它只會認為請求頭和響應頭是HTTP,中間的資料包是tcp,那是因為從不同的層去看,而且你如果是過濾http,中間的tcp資料包不會顯示,但是如果儲存則會顯示中間傳輸的tcp包)。

第三個標誌 - PUSH

存在推送標誌,以確保資料被給予優先順序,並在傳送或接收端處理。這個特定的標誌在資料傳輸的開始和結束時被非常頻繁地使用,影響資料在兩端處理的方式。

當開發人員建立新的應用程式時,他們必須確保它們遵循RFC的特定指導,以確保其應用程式正常工作,並且無缺陷地管理進出OSI模型的應用程式層的資料流。使用時,推送位確保資料段被正確處理,並在虛擬連線的兩端給出適當的優先順序。

當主機發送其資料時,它將暫時排隊在TCP緩衝區中,即儲存器中的一個特殊區域,直到該段達到一定大小,然後傳送到接收器。這種設計保證資料傳輸儘可能高效,而不需要通過建立多個片段而不需要花費時間和頻寬,而是將它們組合成一個或多個較大的片段。

當段到達接收端時,它被傳遞到應用層之前被放置在TCP接收緩衝器中。在進入緩衝區中排隊的資料將保留在那裡,直到緩衝區滿,並且一旦完成,資料就被傳遞到等待它的應用層。

雖然這個過程在大多數情況下執行良好,但是有很多情況下這種“排隊”的資料是不期望的,因為排隊期間的任何延遲都可能導致等待應用程式出現問題。一個簡單的例子將是一個TCP流,例如真正的播放器,其中資料必須立即傳送和處理(由接收器),以確保平滑的流,沒有任何切斷。

這裡提到的最後一點是,Push標誌通常設定在檔案的最後一段,以防止緩衝區死鎖,一直等待資料。當用於通過代理髮送HTTP或其他型別的請求時也可以看到,確保請求得到適當和有效的處理。

第四個標誌位 - 復位(RST)標誌

段到達不用於當前連線時,使用復位標誌。換句話說,如果要向主機發送資料包以建立連線,並且沒有這樣的服務等待在遠端主機上回答,則主機將自動拒絕您的請求,然後向您傳送回覆RST標誌置1。這表示遠端主機已重置連線

雖然這可能是非常簡單和合乎邏輯的,但事實是在大多數情況下,這個“特徵”被大多數黑客使用,以便掃描“開啟”埠的主機。所有現代埠掃描器都能夠通過“復位”功能檢測“開啟”或“收聽”埠。

用於檢測這些埠的方法非常簡單:當嘗試掃描遠端主機時,將使用SYN標誌位(1)構建有效的TCP段,並將其傳送到目標主機。如果沒有服務監聽特定埠上的入站連線,則遠端主機將回復ACK和RST標誌設定(1)。另一方面,如果埠上有服務監聽,則遠端主機將構造一個具有ACK標誌位的TCP段(1)。這是標準三次握手的一部分。

一旦主機掃描開放埠接收到該段,它將完成三次握手,然後使用FIN(見下文)標誌終止它,並將特定埠標記為“活動”。

第五個標誌位 - 同步標誌(SYNCHRONISATION FLAG)

TCP標誌選項中包含的第五個標誌可能是TCP通訊中使用最知名的標誌。在建立兩臺主機之間的經典3次握手時,SYN標誌的作用是初始化TCP連線的。

 

在上圖中,主機A需要使用TCP作為其傳輸協議從主機B下載資料。TCP協議需要進行三次握手,以便雙方建立虛擬連線才能交換資料。在三次握手期間,我們可以統計每個主機發送的SYN標誌總數為2個。隨著檔案的交換和新連線的建立,我們將看到更多的SYN標誌被髮送和接收。

在傳送SYN資料包時,並且建立一個隨機的序列號

第六個標誌位 - FIN 標誌

可用的最後一個標誌是FIN標誌,代表“完成”一詞。該標誌用於拆除使用上一個標誌(SYN)建立的虛擬連線,因此,由於這個原因,當連線之間交換最後一個數據包時,總是出現FIN標誌。

重要的是要注意,當主機發送FIN標誌來關閉連線時,它可能會繼續接收資料,直到遠端主機也關閉了連線,儘管這僅在特定情況下發生。一旦連線被雙方削減,則釋放用於連線的每端的緩衝區。

正常的拆卸過程如下所示:

 

上圖顯示了主機A和B之間的現有連線,兩個主機正在交換資料。一旦資料傳輸完成,主機A傳送一個包含FIN,ACK標誌的資料包(步驟1)。使用此資料包,主機A確認先前的流,同時啟動TCP關閉過程以終止此連線。此時,Host A的應用程式將停止接收任何資料,並將關閉此端的連線。為了響應主機A關閉連線的請求,主機B將傳送確認(STEP 2),並通知其應用程式連線不再可用。一旦完成,主機(B)將傳送自己的FIN,ACK標誌(步驟3)以關閉其部分連線。

如果您想知道為什麼需要執行此過程,則可能需要記住,TCP是一個全雙工連線,這意味著有兩個方向的資料流。在我們的示例中,這是從主機A到主機B的連線流,反之亦然。另外,它需要兩個主機關閉它們的連線,因此兩個主機必須傳送一個FIN標誌並且另一個主機必須確認它的原因。

最後,在步驟4,主機A將確認在步驟3傳送的主機B的請求,並且雙方的已關閉程式現在已經完成。

然後詳細回顧一下TCP三次握手和四次揮手,此過程涉及到標誌位的變化。

TCP三次握手

三次握手Three-way Handshake 

一個虛擬連線的建立是通過三次握手來實現的 

1. (A) --> SYN --> (B) 

2. (A) <--SYN/ACK <-- (B) 

3. (A) --> ACK --> (B) 

第一次握手:主機A傳送位碼為SYN=1,隨機產生seq number=1234567(隨機數)的資料包到伺服器,主機B由SYN=1知道,A要求建立聯機;

一個 SYN包就是僅SYN標記設為1的TCP包

第二次握手:主機B收到請求後要確認聯機資訊,向A傳送ack number=(主機A的seq number+1),SYN=1,ACK=1,隨機產生seq number=7654321(隨機數)的包;

SYN/ACK包是僅SYN 和 ACK 標記為1的包. 

第三次握手:主機A收到後檢查ack number是否正確,即第一次傳送的seq number+1,以及位碼ACK是否為1,若正確,主機A會再發送ack number=(主機B的seq number+1),ACK=1,主機B收到後確認seq值正確,並且ACK=1則連線建立成功。

ACK包就是僅ACK 標記設為1的TCP包

完成三次握手,主機A與主機B開始傳送資料。並且每一個數據包中都帶有ACK標誌位。

這就是為何連線狀態跟蹤很重要的原因了。 沒有連線跟蹤,防火牆將無法判斷收到的ACK包是否屬於一個已經建立的連線.一般的包過濾(Ipchains)收到ACK包時,會讓它通過(這絕對不是個 好主意). 而當狀態型防火牆收到此種包時,它會先在連線表中查詢是否屬於哪個已建連線,否則丟棄該包。 

未連線佇列

在三次握手協議中,伺服器維護一個未連線佇列,該佇列為每個客戶端的SYN包(syn=j)開設一個條目,該條目表明伺服器已收到SYN包,並向客戶發出確認,正在等待客戶的確認包時,刪除該條目,伺服器進入ESTAB_LISHED狀態。常用於產生SYN flooding攻擊,所以縮短SYN超時時間,減小SYN佇列數是抵抗SYN dos的基本手段。

TCP四次揮手

四次握手Four-way Handshake 

四次握手用來關閉已建立的TCP連線 

1. (B) --> ACK/FIN --> (A) 

2. (B) <-- ACK <-- (A) 

3. (B) <-- ACK/FIN <-- (A) 

4. (B) --> ACK --> (A) 

四次揮手過程說明: 
1、客戶端傳送斷開TCP連線請求的報文,其中報文中包含seq序列號,並且還將報文中的FIN欄位置為1,表示需要斷開TCP連線。(FIN=1,seq=x,x序列號為客戶端傳送的上一個資料包中的確認號值。ACK=1, ack確認號為伺服器傳送的上一個資料包中的序列號+該資料包所帶的資料的大小

2、服務端會回覆客戶端傳送的TCP斷開請求報文,其包含seq序列號,而且會產生ACK欄位,ACK欄位數值是在客戶端發過來的seq序列號基礎上加1進行回覆,以便客戶端收到資訊時,知曉自己的TCP斷開請求已經得到驗證。(ACK=1,ack=x+1,seq=第1步中的確認號值)

3、服務端在回覆完客戶端的TCP斷開請求後,不會馬上進行TCP連線的斷開,服務端會先確保斷開前,所有傳輸到A的資料是否已經傳輸完畢,一旦確認傳輸資料完畢,就會將回復報文的FIN欄位置1,ACK為1。並且產生seq序列號。(FIN=1,ACK=1,ack確認號為客戶端傳送的上一個資料包中的序列號+該資料包所帶資料的大小,seq=z,z序列號為伺服器傳送的上一個資料包中的確認號值

4、客戶端收到服務端的TCP斷開請求後,會回覆服務端的斷開請求,包含seq欄位和ack欄位,ack欄位會在服務端的TCP斷開請求的seq基礎上加1,第3步資料包中的序列號+1,從而完成服務端請求的驗證回覆。(FIN=1,ACK=z+1,seq=h,h為第3步中的確認號值) 
至此TCP斷開的4次揮手過程完畢。

注意: 由於TCP連線是雙向連線, 因此關閉連線需要在兩個方向上做。ACK/FIN 包(ACK 和FIN 標記設為1)通常被認為是FIN(終結)包。然而,由於連線還沒有關閉, FIN包總是打上ACK標記。沒有ACK標記而僅有FIN標記的包不是合法的包,並且通常被認為是惡意的。 

注意 : 因為FIN和SYN一樣,也要佔一個序號。理論上伺服器在TCP連線關閉時傳送的終止資料包中,只有終止位是置1,然後客戶端進行確認。但是在實際的 TCP實現中,在終止資料包中,確認位和終止位是同時置為1的,確認位置為1表示對最後一次傳輸的資料進行確認,終止位置為1表示關閉該方向的TCP連 接。

TCP狀態轉移

è¿éåå¾çæè¿°

建立連線

1、一開始,建立連線之前伺服器和客戶端的狀態都為CLOSED; 
2、伺服器建立socket後開始監聽,變為LISTEN狀態; 
3、客戶端請求建立連線,向伺服器傳送SYN報文,客戶端的狀態變味SYN_SENT; 
4、伺服器收到客戶端的報文後向客戶端傳送ACK和SYN報文,此時伺服器的狀態變為SYN_RCVD; 
5、然後,客戶端收到ACK、SYN,就向伺服器傳送ACK,客戶端狀態變為ESTABLISHED; 
6、伺服器端收到客戶端的ACK後變為ESTABLISHED。此時3次握手完成,連線建立!

由於TCP連線是全雙工的,斷開連線會比建立連線麻煩一點點。 
1、客戶端先向伺服器傳送FIN報文,請求斷開連線,其狀態變為FIN_WAIT1; 
2、伺服器收到FIN後向客戶端傳送ACK,伺服器的狀態圍邊CLOSE_WAIT; 
3、客戶端收到ACK後就進入FIN_WAIT2狀態,此時連線已經斷開了一半了。如果伺服器還有資料要傳送給客戶端,就會繼續傳送; 
4、直到發完資料,就會發送FIN報文,此時伺服器進入LAST_ACK狀態; 
5、客戶端收到伺服器的FIN後,馬上傳送ACK給伺服器,此時客戶端進入TIME_WAIT狀態; 
6、再過了2MSL長的時間後進入CLOSED狀態。伺服器收到客戶端的ACK就進入CLOSED狀態。 
至此,還有一個狀態沒有出來:CLOSING狀態。 
CLOSING狀態表示: 
客戶端傳送了FIN,但是沒有收到伺服器的ACK,卻收到了伺服器的FIN,這種情況發生在伺服器傳送的ACK丟包的時候,因為網路傳輸有時會有意外。

狀態詳解:

LISTEN:等待從任何遠端TCP 和埠的連線請求。

SYN_SENT:傳送完一個連線請求後等待一個匹配的連線請求。

SYN_RECEIVED:傳送連線請求並且接收到匹配的連線請求以後等待連線請求確認。

ESTABLISHED:表示一個開啟的連線,接收到的資料可以被投遞給使用者。連線的資料傳輸階段的正常狀態。

FIN_WAIT_1:等待遠端TCP 的連線終止請求,或者等待之前傳送的連線終止請求的確認。

FIN_WAIT_2:等待遠端TCP 的連線終止請求。

CLOSE_WAIT:等待本地使用者的連線終止請求。

CLOSING:等待遠端TCP 的連線終止請求確認。

LAST_ACK:等待先前傳送給遠端TCP 的連線終止請求的確認(包括它位元組的連線終止請求的確認)

TIME_WAIT:等待足夠的時間過去以確保遠端TCP 接收到它的連線終止請求的確認。
TIME_WAIT 兩個存在的理由:
          1.可靠的實現tcp全雙工連線的終止;
          2.允許老的重複分節在網路中消逝。

CLOSED:不在連線狀態(這是為方便描述假想的狀態,實際不存在)

【問題1】為什麼連線的時候是三次握手,關閉的時候卻是四次握手?
答:因為當Server端收到Client端的SYN連線請求報文後,可以直接傳送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連線時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都發送完了,我才能傳送FIN報文,因此不能一起傳送。故需要四步握手。

【問題2】為什麼TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?

答:雖然按道理,四個報文都發送完畢,我們可以直接進入CLOSE狀態了,但是我們必須認為網路是不可靠的,有可以最後一個ACK丟失。所以TIME_WAIT狀態就是用來重發可能丟失的ACK報文。

TCP會話開始和結束

(1)如上三次揮手和四次握手所示,開始都是以SYN開始,結束以FIN結束。

(2)發生異常情況下都是以RST結束。

在TCP協議中,RST標識復位,用來異常的關閉連結。在TCP的設計中它是不可或缺的,傳送RST包關閉連結時,不必等緩衝區的資料都發送出去。直接丟棄緩衝區中的資料,傳送RST包。而接受段收到RST包後,也不必傳送ACK包來確認。

1. 請求的目標埠未開啟,服務端傳送RST包(用於埠掃描)。

2. 請求超時。

3. Socket核心接收緩衝區Recv-Q中的資料未完全被應用程式讀取,而關閉該Socket。

4. 向已關閉的Socket中傳送資料。(send與close呼叫)如果呼叫close()方法,關閉的Socket代表的連結依然處於FIN_WAIT2狀態,則正常返回ACK確認包。如果狀態FIN_WAIT2的超時,則close()呼叫後【向FIN_WAIT2超時狀態的連線傳送FIN】,依然會返回RST包。

連線復位 Resetting a connection 

四次握手不是關閉TCP連線的唯一方法。有時,如果主機需要儘快關閉連線(或連線超時,埠或主機不可達),RST (Reset)包將被髮送。注意與FIN不同,由於RST包不是TCP連線中的必須部分, 可以只發送RST包(即不帶ACK標記)。 但在正常的TCP連線中RST包也可以帶ACK確認標記 。請注意RST包是可以不要收到方確認的。

無效的TCP標記 Invalid TCP Flags 

(1)ACK是可能與SYN,FIN等同時使用的,比如SYN和ACK可能同時為1,它表示的就是建立連線之後的響應,如果只是單個的一個SYN,它表示的只是建立連線。TCP的幾次握手就是通過這樣的ACK表現出來的。但SYN與FIN是不會同時為1的,因為前者表示的是建立連線,而後者表示的是斷開連線。

(2)RST一般是在FIN之後才會出現為1的情況,表示的是連線重置。一般地,當出現FIN包或RST包時,我們便認為客戶端與伺服器端斷開了連線;而當出現SYN和SYN+ACK包時,我們認為客戶端與伺服器建立了一個連線

(3)PSH為1的情況,一般只出現在 DATA內容不為0的包中,也就是說PSH為1表示的是有真正的TCP資料包內容被傳遞。 

(4)TCP的連線建立和連線關閉,都是通過請求-相應的模式完成的。最常見的非法組合是SYN/FIN 包。注意:由於 SYN包是用來初始化連線的,它不可能和 FIN和RST標記一起出現,這是一個惡意攻擊。 由 SYN/FIN 包,我們知道別的一些組合,例如SYN/FIN/PSH, SYN/FIN/RST, SYN/FIN/RST/PSH。很明顯,這些也都是惡意的攻擊。 

(5)別的已知的非法包有FIN (無ACK標記)和"NULL"包。由於ACK/FIN包的出現是為了關閉一個TCP連線,那麼正常的FIN包總是帶有 ACK 標記。"NULL"包就是沒有任何TCP標記的包(URG,ACK,PSH,RST,SYN,FIN都為0)。 

我們自己實現的socket應怎麼確定傳輸結束那?

一種辦法是伺服器傳送結束就直接斷開連線,但是如果還有其他業務需要通訊這種辦法不太靠譜;
另一種辦法是,伺服器先給客戶端傳送檔案的大小,然後客戶端通過這個大小自己決定何時結束,例如FTP協議,客戶端再請求檔案可以再 connect 重新建立一條連結來,把傳控制協議的通道和傳資料的通道分開。.可以提前定義好檔名、檔案大小和檔案md5(用來檢驗),傳送給客戶端,然後讓客戶端去斷開連線。

  • 呼叫socket的 shutdownOutput 方法(Java)關閉輸出流,該方法的文件說明為,將此套接字的輸出流置於“流的末尾”,這樣另一端的輸入流上的read操作就會返回-1。
  • 約定結束標誌,當讀到該結束標誌時退出不再read。 (Http 的 Transfer-Encoding: Chunked 首部,表示將以一個 length 為 0 的 chunk 做結束標誌)
  • 設定超時(timeout),會在設定的超時時間到達後丟擲SocketTimeoutException異常而不再阻塞。
  • 雙方定義好通訊協議,在協議頭部約定好資料的長度。當讀取到的長度等於這個長度時就不再繼續呼叫read方法。(Http 的 content-length 首部,會給出主體的長度)