TCP報文結構
- 源端口和目的端口:各佔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三次握手html
整個流程爲:緩存
- 客戶端主動打開,發送鏈接請求報文段,將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步中客戶端還要再進行一次確認呢?服務器
這主要是爲了防止已經失效的鏈接請求報文段忽然又傳回到服務端而產生錯誤的場景:所謂"已失效的鏈接請求報文段"是這樣產生的。正常來講,客戶端發出鏈接請求,但由於鏈接請求報文丟失而未收到確認。因而客戶端再次發出一次鏈接請求,後來收到了確認,創建了鏈接。數據傳輸完畢後,釋放了鏈接,客戶端一共發送了兩個鏈接請求報文段,其中第一個丟失,第二個到達了服務端,沒有"已失效的鏈接請求報文段"。網絡
如今假定一種異常狀況,即客戶端發出的第一個鏈接請求報文段並無丟失,只是在某些網絡節點長時間滯留了,以致於延誤到鏈接釋放之後的某個時間點纔到達服務端。原本這個鏈接請求已經失效了,可是服務端收到此失效的鏈接請求報文段後,就誤認爲這是客戶端又發出了一次新的鏈接請求。因而服務端又向客戶端發出請求報文段,贊成創建鏈接。假定不採用三次握手,那麼只要服務端發出確認,鏈接就創建了。post
因爲如今客戶端並無發出鏈接創建的請求,所以不會理會服務端的確認,也不會向服務端發送數據,可是服務端卻覺得新的傳輸鏈接已經創建了,並一直等待客戶端發來數據,這樣服務端的許多資源就這樣白白浪費了。spa
採用三次握手的辦法能夠防止上述現象的發生。好比在上述的場景下,客戶端不向服務端的發出確認請求,服務端因爲收不到確認,就知道客戶端並無要求創建鏈接。3d
TCP四次揮手指針
TCP三次握手是TCP鏈接創建的過程,TCP四次揮手則是TCP鏈接釋放的過程。下面是TCP四次揮手的流程圖:htm
當客戶端沒有數據再須要發送給服務端時,就須要釋放客戶端的鏈接,這整個過程爲:blog
- 客戶端發送一個報文給服務端(沒有數據),其中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字節。
TCP 三次握手原理,你真的理解嗎?