來自公衆號:架構文摘
TCP協議全稱爲:Transmission Control Protocol
,是一種面向鏈接、保證數據傳輸安全、可靠的數據傳輸協議。爲了確保數據的可靠傳輸,不僅需要對發出的每個字節進行編號確認,還需要驗證每一個數據包的有效性。每個TCP數據包是封閉在IP包中的,每個一IP包的後面緊跟着的是TCP頭,TCP報文格式如下:
源端口和目的端口字段
TCP源端口(Source Port):源計算機上的應用程序的端口號,佔 16 位。
TCP目的端口(Destination Port):目標計算機的應用程序端口號,佔 16 位。
序列號字段
CP序列號(Sequence Number):佔 32 位。它表示本報文段所發送數據的第一個字節的編號。在 TCP 連接中,所傳送的字節流的每一個字節都會按順序編號。當SYN標記不爲1時,這是當前數據分段第一個字母的序列號;如果SYN的值是1時,這個字段的值就是初始序列值(ISN),用於對序列號進行同步。這時,第一個字節的序列號比這個字段的值大1,也就是ISN加1。
確認號字段
TCP 確認號(Acknowledgment Number,ACK Number):佔 32 位。它表示接收方期望收到發送方下一個報文段的第一個字節數據的編號。其值是接收計算機即將接收到的下一個序列號,也就是下一個接收到的字節的序列號加1。
數據偏移字段
TCP 首部長度(Header Length):數據偏移是指數據段中的“數據”部分起始處距離 TCP 數據段起始處的字節偏移量,佔 4 位。其實這裏的“數據偏移”也是在確定 TCP 數據段頭部分的長度,告訴接收端的應用程序,數據從何處開始。
保留字段
保留(Reserved):佔 4 位。爲 TCP 將來的發展預留空間,目前必須全部爲 0。
標誌位字段
CWR(Congestion Window Reduce):擁塞窗口減少標誌,用來表明它接收到了設置 ECE 標誌的 TCP 包。並且,發送方收到消息之後,通過減小發送窗口的大小來降低發送速率。
ECE(ECN Echo):用來在 TCP 三次握手時表明一個 TCP 端是具備 ECN 功能的。在數據傳輸過程中,它也用來表明接收到的 TCP 包的 IP 頭部的 ECN 被設置爲 11,即網絡線路擁堵。
URG(Urgent):表示本報文段中發送的數據是否包含緊急數據。URG=1 時表示有緊急數據。當 URG=1 時,後面的緊急指針字段纔有效。
ACK:表示前面的確認號字段是否有效。ACK=1 時表示有效。只有當 ACK=1 時,前面的確認號字段纔有效。TCP 規定,連接建立後,ACK 必須爲 1。
PSH(Push):告訴對方收到該報文段後是否立即把數據推送給上層。如果值爲 1,表示應當立即把數據提交給上層,而不是緩存起來。
RST:表示是否重置連接。如果 RST=1,說明 TCP 連接出現了嚴重錯誤(如主機崩潰),必須釋放連接,然後再重新建立連接。
SYN:在建立連接時使用,用來同步序號。當 SYN=1,ACK=0 時,表示這是一個請求建立連接的報文段;當 SYN=1,ACK=1 時,表示對方同意建立連接。SYN=1 時,說明這是一個請求建立連接或同意建立連接的報文。只有在前兩次握手中 SYN 才爲 1。
FIN:標記數據是否發送完畢。如果 FIN=1,表示數據已經發送完成,可以釋放連接。
窗口大小字段
窗口大小(Window Size):佔 16 位。它表示從 Ack Number 開始還可以接收多少字節的數據量,也表示當前接收端的接收窗口還有多少剩餘空間。該字段可以用於 TCP 的流量控制。
TCP 校驗和字段
校驗位(TCP Checksum):佔 16 位。它用於確認傳輸的數據是否有損壞。發送端基於數據內容校驗生成一個數值,接收端根據接收的數據校驗生成一個值。兩個值必須相同,才能證明數據是有效的。如果兩個值不同,則丟掉這個數據包。Checksum 是根據僞頭 + TCP 頭 + TCP 數據三部分進行計算的。
緊急指針字段
緊急指針(Urgent Pointer):僅當前面的 URG 控制位爲 1 時纔有意義。它指出本數據段中爲緊急數據的字節數,佔 16 位。當所有緊急數據處理完後,TCP 就會告訴應用程序恢復到正常操作。即使當前窗口大小爲 0,也是可以發送緊急數據的,因爲緊急數據無須緩存。
可選項字段
選項(Option):長度不定,但長度必須是 32bits 的整數倍。
TCP建立連接
TCP建立連接需要三個步驟,也就是大家熟知的三次握手。下圖了正常情形下通過三次握手建立連接的過程:
A機器發出一個數據包
SYN
設置爲1,表示希望建立連接。這個包中的假設seq爲x
。機器A發送
SYN
數據包後,會進入SYN_SENT
狀態B機器收到A發送的
SYN
數據後,響應一個數據包將SYN
和ACK
設置爲1,假設這個響應包的序列號爲y
,同時期望下一次收到的數據庫的序列爲x+1
B回覆響應包後,進入
SYN_RECD
狀態A收到B的響應包後,對響應包做應答將
ACK
標誌設置爲1,序列號爲x + 1
,期望下一次收到的數據包的序列號爲y+1
A機器和B機器連接建立成功
TCP三次握手抓包驗證
以爲驗證三次握手是否描述正確,在下使用Wireshark
進行抓包驗證。首先使用ping
命令獲取www.baidu.com
的ip地址:
正在 Ping www.a.shifen.com [183.232.231.174] 具有 32 字節的數據:
來自 183.232.231.174 的回覆: 字節=32 時間=16ms TTL=54
來自 183.232.231.174 的回覆: 字節=32 時間=16ms TTL=54
來自 183.232.231.174 的回覆: 字節=32 時間=16ms TTL=54
183.232.231.172 的 Ping 統計信息:
數據包: 已發送 = 3,已接收 = 3,丟失 = 0 (0% 丟失),
往返行程的估計時間(以毫秒爲單位):
最短 = 16ms,最長 = 16ms,平均 = 16ms
以上輸出顯示www.baidu.com
的ip地址:183.232.231.174
,然後使用Wireshark
的過濾器僅顯示與www.baidu.com
通信的tcp
數據包:
ip.src_host == "183.232.231.174" or ip.dst_host == "183.232.231.174" and tcp
使用Wireshark
抓包分析後,驗證TCP正常連接三次握手與上節描述的一致。
爲什麼是三次握手?
爲什麼是三次握手?三次握手主要有兩個目的:信息對等和防止超時。
信息對等
兩臺機器通信時都需要確認四個信息:
自己發報文的能力
自己收報文的能力
對方發報文的能力
對方收報文的通知
第一次握手
第一次握手A機器向B機器發送SYN
數據包,此時只有B機器能確認自己收報文的能力和對方發報文的能力。
一次握手完成B機器能夠確認的信息有:
√B機器收報文的能力
√A機器發報文的能力
第二次握手
每二次握手後B響應A機器的SYN
數據包,此時A機器就能確認:自己發報文的能力、自己收報文的能力、對方發報文的能力、對方收報文的能力
二次握手完成A機器能夠確認的信息有:
√A機器發報文的能力
√A機器收報文的能力
√B機器發報文的能力
√B機器收報文的能力
第三次握手
每三次握手後A應答B機器的SYN + ACK
數據包,此時B機器就能確認:自己發報文的能力、對方收報文的能力
三次握手完成B機器能夠確認的信息有:
√B機器發報文的能力
√A機器收報文的能力
至此經過三次握手A、B機器就能做到信息對等,雙方都能確認自己和對方的收、發報文的能力,最後方便理解將信息對等製作成一個小表格:
防止超時
三次握手除了保證信息對等也是了防止請求超時導致髒連接。TTL網絡報文的生存往往會超過TCP請求超時時間,如果兩次握手就能創建連接,傳輸數據並釋放連接後,第一個超時的連接請求才到達B機器,B機器 會以爲是 A 創建新連接的請求,然後確認同意創建連接。因爲A機器的狀態不是SYN_SENT
,所以會直接丟棄了B的確認數據,導致 B 機器單方面的創建連接完畢。
如果是三次握手,則 B 機器收到連接請求後,同樣會向 A 機器確同意創建連接,但因爲 A 不是SYN_SENT
狀態,所以 A機器 不會回覆 B 機器確認創建連接請求,而 B 機器到一段時間後由於長時間沒有收到確認信息,最終會導致連接創建失敗,因此不會出現髒連接。
TCP斷開連接
TCP是全雙工通信,雙方都能作爲數據的發送方和接收方,但TCP會有斷開的時候。TCP建立連接需要三次握手而斷開連接卻要四次,如圖所示爲TCP斷開連接四次揮手過程:
A 機器發送關閉數據包將
FIN
設置爲1,假設序列號爲u
,發完關閉數據包後此時 A 機器處理FIN_WAIT_1
狀態B 收到關閉連接請求後,通知應用程序處理完剩下的數據
B 響應 A 的關閉連接請求,將
ACK
標誌設置爲1,seq爲v
,ack爲u+1
,隨後 B 機器處於CLOSE_WAIT
狀態A 收到應答後,處於
FIN_WAIT_2
狀態,繼續等待 B 機器的FIN
數據包B 處理好現場後,主動向 A 機器發送數據包,並將
FIN
和ACK
標誌設置爲1,seq爲w
,ack爲u+1
,隨後處於LAST_WAIT
狀態等待 A 機器的應答A 機器收到
FIN
數據包後,隨後發送ACK
數據包,seq爲u+1
,ack爲w+1
, 此時 A 機器處理TIME_WAIT
狀態B 機器收到
ACK
響應包後,進行CLOSED
狀態,連接正常關閉A 機器在
TIME_WAIT
狀態等待2MSL
後,也進入CLOSEED
狀態,連接關閉
什麼是2MSL:MSL是Maximum Segment Lifetime英文的縮寫,中文可以譯爲“報文最大生存時間”,
2MSL即兩倍的MSL
四次揮手斷開連接可以用更形象的方式來表達:
男生 :我們分手吧。
女生 :好的,我需要去家裏把東西收拾完,再發消息給你。(此時男生不能再擁抱女生)
。。。,一個小時後
女生 :我收拾完了,分手吧(此時女生也不能再擁抱男生)
男生:好的(此時雙方約定一段時間後,纔可以分別找新的對象)
TCP四次揮手抓包驗證
抓包過程與與三次握手抓包過程一致,這裏不描述。直接看訪問後抓包的截圖:
第一個包是由
192.168.1.6
這臺機器(也就是客戶機),發送了一個FIN
包,seq爲80
,ack爲2782
第二個包由
183.232.231.174
(服務器),對192.168.1.6
這臺機器(也就是客戶機)發送了一個ACK
包,seq爲2782
,ack爲81
第三個包由
183.232.231.174
(服務器),對192.168.1.6
這臺機器(也就是客戶機)發送了一個ACK
和FIN
包,seq爲2782
,ack爲81
第四個包由
192.168.1.6
,向服務器響應了一個ACK
包,seq爲81
,ack爲2783
四次揮手流程與我們描述的一致。
TIME_WAIT 狀態
主動要求關閉的機器(機器A)表示收到對方的FIN
報文後,併發送出ACK
報文後,進行TIME_WAIT
狀態,等待2MSL
後進行CLOSED
狀態。如果在TIME_WAIT_1
時收到FIN
標誌和ACK
標誌報文時,可以直接進入TIME_WAIT
狀態,而無需進入TIME_WAIT_2
狀態。
爲什麼要有 TIME_WAIT
確認被動關閉(機器B)能夠順利進入CLOSED
狀態
假如A機器發送最後一個ACK
後,但由於網絡原因ACK
包未能到達 B 機器,此時 B機器通常會認爲 A機器 沒有收到 FIN+ACK
報文,會重發一次FIN+ACK
報文。如果 A機器 發送最後一個ACK
後,自私的關閉連接進入 CLOSED
狀態,就可能導致 B 無法收到ACK
報文,無法正常關閉。
防止失效請求
TIME_WAIT 狀態可以防止已失效的請求包與正常連接的請求數據包混淆而發生異常。因爲TIME_WAIT 狀態無法真正釋放句柄資源,在此期間, Socket中使用的本地端口在默認情況下不能再被使用。
CLOSE_WAIT 狀態
被動關閉的機器(機器B)在收到對方發送的,FIN
報文後,馬上回復ACK
報文,進入CLOSE_WAIT
狀態。通知應用程序,處理剩下的數據,釋放資源。