- 源端口和目的端口:各占2個字節,分別寫入源端口號和目的端口號。
- 序號:占4個字節。序號使用mod
運算。TCP是面向字節流的,在一個TCP連接中傳送的字節流中的每一個字節都按順序編號。故該字段也叫做“報文段序號”。
- 確認序號:占4個字節,是期望收到對方下一個報文段的第一個數據字節的序號。若確認序號=N,則表明:到序號N-1為止的所有數據都已正確收到。
- 數據偏移:占4位,表示TCP報文段的首部長度。注意,“數據偏移”的單位是32位字(即以4字節長的字為計算單位)。故TCP首部的最大長度為60字節。
- 保留:占6位,保留為今后使用,目前置為0;
- 緊急URG:當URG=1,表明緊急指針字段有效。這時發送方TCP就把緊急數據插入到本報文段數據的最前面,而在緊急數據后面的數據仍是普通數據。
- 確認ACK:當ACK=1時,確認字段才有效。當ACK=0時,確認號無效。TCP規定,在連接建立后所有傳送的報文段都必須把ACK置1。
- 推送PSH:接收方TCP收到PSH=1的報文段,就盡快地交付給接收應用進程,而不再等到整個緩存都填滿了后再向上交付。
- 復位RST:當RST=1時,表明TCP連接中出現嚴重差錯,必須釋放連接,然后再重新建立運輸連接。
- 同步SYN:在連接建立時用來同步序號。當SYN=1而ACK=0時,表明這是一個連接請求報文段。對方若同意建立連接,則應在響應的報文段中使SYN=1和ACK=1。故SYN置為1,就表示這是一個連接請求和連接接收報文。
- 終止FIN:用來釋放連接。當FIN=1時,表明此報文段的發送方的數據已發送完畢,並要求釋放運輸連接。
- 窗口:占2個字節。窗口值作為接收方讓發送方設置其發送窗口的依據。
- 檢驗和:占2字節。檢驗和字段檢驗的范圍包括首部和數據這兩部分。和UDP數據報一樣,在計算檢驗和時,也要在TCP報文段的前面加上12字節的偽首部。偽首部的格式與UDP用戶數據報的偽首部一樣,但要將偽首部第四個字段中的17 改為6(協議號),把第5字段中的UDP長度改為TCP長度。
- 緊急指針:占2字節。緊急指針僅在URG=1時才有意義,它指出本報文段中的緊急數據的字節數。
TCP三次握手
整個流程為:
- 客戶端主動打開,發送連接請求報文段,將SYN標識位置為1,Sequence Number置為x(TCP規定SYN=1時不能攜帶數據,x為隨機產生的一個值),然后進入SYN_SEND狀態
- 服務器收到SYN報文段進行確認,將SYN標識位置為1,ACK置為1,Sequence Number置為y,Acknowledgment Number置為x+1,然后進入SYN_RECV狀態,這個狀態被稱為半連接狀態
- 客戶端再進行一次確認,將ACK置為1(此時不用SYN),Sequence Number置為x+1,Acknowledgment Number置為y+1發向服務器,最后客戶端與服務器都進入ESTABLISHED狀態
為什么在第3步中客戶端還要再進行一次確認呢?
這主要是為了防止已經失效的連接請求報文段突然又傳回到服務端而產生錯誤的場景:所謂"已失效的連接請求報文段"是這樣產生的。正常來說,客戶端發出連接請求,但因為連接請求報文丟失而未收到確認。於是客戶端再次發出一次連接請求,后來收到了確認,建立了連接。數據傳輸完畢后,釋放了連接,客戶端一共發送了兩個連接請求報文段,其中第一個丟失,第二個到達了服務端,沒有"已失效的連接請求報文段"。
現在假定一種異常情況,即客戶端發出的第一個連接請求報文段並沒有丟失,只是在某些網絡節點長時間滯留了,以至於延誤到連接釋放以后的某個時間點才到達服務端。本來這個連接請求已經失效了,但是服務端收到此失效的連接請求報文段后,就誤認為這是客戶端又發出了一次新的連接請求。於是服務端又向客戶端發出請求報文段,同意建立連接。假定不采用三次握手,那么只要服務端發出確認,連接就建立了。
由於現在客戶端並沒有發出連接建立的請求,因此不會理會服務端的確認,也不會向服務端發送數據,但是服務端卻以為新的傳輸連接已經建立了,並一直等待客戶端發來數據,這樣服務端的許多資源就這樣白白浪費了。
采用三次握手的辦法可以防止上述現象的發生。比如在上述的場景下,客戶端不向服務端的發出確認請求,服務端由於收不到確認,就知道客戶端並沒有要求建立連接。
TCP四次揮手
TCP三次握手是TCP連接建立的過程,TCP四次揮手則是TCP連接釋放的過程。下面是TCP四次揮手的流程圖:
當客戶端沒有數據再需要發送給服務端時,就需要釋放客戶端的連接,這整個過程為:
- 客戶端發送一個報文給服務端(沒有數據),其中FIN設置為1,Sequence Number置為u,客戶端進入FIN_WAIT_1狀態
- 服務端收到來自客戶端的請求,發送一個ACK給客戶端,Acknowledge置為u+1,同時發送Sequence Number為v,服務端年進入CLOSE_WAIT狀態
- 服務端發送一個FIN給客戶端,ACK置為1,Sequence置為w,Acknowledge置為u+1,用來關閉服務端到客戶端的數據傳送,服務端進入LAST_ACK狀態
- 客戶端收到FIN后,進入TIME_WAIT狀態,接着發送一個ACK給服務端,Acknowledge置為w+1,Sequence Number置為u+1,最后客戶端和服務端都進入CLOSED狀態
為什么建鏈接要3次握手,斷鏈接需要4次揮手?
- 對於建鏈接的3次握手,主要是要初始化Sequence Number 的初始值。通信的雙方要互相通知對方自己的初始化的Sequence Number(縮寫為ISN:Inital Sequence Number)——所以叫SYN,全稱Synchronize Sequence Numbers。也就上圖中的 x 和 y。這個號要作為以后的數據通信的序號,以保證應用層接收到的數據不會因為網絡上的傳輸的問題而亂序(TCP會用這個序號來拼接數據)。
- 對於4次揮手,其實你仔細看是2次,因為TCP是全雙工的,所以,發送方和接收方都需要Fin和Ack。只不過,有一方是被動的,所以看上去就成了所謂的4次揮手。如果兩邊同時斷連接,那就會就進入到CLOSING狀態,然后到達TIME_WAIT狀態。下圖是雙方同時斷連接的示意圖(你同樣可以對照着TCP狀態機看):
使用Wireshark抓包驗證TCP三次握手過程
為了加深對TCP三次握手的理解,抓包看一下TCP三次握手的過程。
抓包下來的內容為:
這里多說一句,由於wireshark抓包針對的是網卡,因此只要某張網卡上有網絡訪問,就會有數據包,這會導致Wireshark的抓包結果里面會有大量數據包,而大多數都不是想要的,這種情況可以使用Wireshark的過濾規則。我這里由於知道目標ip,因此使用的是"ip.src == xxx.xxx.xxx.xxx or ip.dst == xxx.xxx.xxx.xxx"這條規則只過濾特定的ip。
從抓包結果看來,整個過程符合TCP三次握手的預期:
- 客戶端發送SYN給服務端
- 服務端返回SYN+ACK給客戶端
- 客戶端確認,返回ACK給服務端
至於Sequence Number和Acknowledge Number就不看了,但是注意,前面說了Sequence Number是隨機產生的一個值,但是這里確是0,不光這里是0,抓其他的任何包這個值都是0。但其實這里並不是真的0,而是Wireshark為了顯示更好閱讀,使用了relative sequence number相對序號,Sequence Number具體值我們也是可以看到的:
第一個紅框就是上面說的relative sequence number,第二個紅框就是Sequence Number的真實值0xc978aa7e,轉換為十進制為3380128382,就是隨機產生的Sequence Number。
順便能看到,下一個數據包就是HTTP的數據包,因為TCP三次握手已完成,連接建立,正式傳輸應用層數據,傳輸的HTTP內容大小為704字節。