本文分享自華為雲社群《輸入網址,小手一點,後面到底發生了什麼?-雲社群-華為雲》,作者:龍哥手記。
輸入網址并點回車,背景到底發生了什麼。透析 HTTP 協定與 TCP 連接配接之間的千絲萬縷的關系。掌握為何是三次握手四次揮手?time_wait 存在的意義是什麼?全面圖解重點問題,再也不用擔心面試問這個問題。
大緻流程
- URL 解析,解析 http 協定、端口、資源位址。
- DNS 查詢:首先查詢本地 host,再通路 DNS 伺服器将 域名解析成 ip 位址。
- 建立 TCP 連接配接。
- 伺服器收到請求後處理,并且構造響應傳回給用戶端。
- 用戶端接收 HTTP 封包響應。
- 渲染頁面,最後有可能會四次揮手斷開連接配接,也可能不會而是複用連接配接。
重點來了:
- 如何了解 TCP 的三次握手與四次揮手?每次握手用戶端與服務端是怎樣的狀态?
- 為何揮手會出現 2MSL,遇到大量 Socket 處在 TIME_WAIT 或者 CLOSE_WAIT 狀态是什麼問題?
- 三次握手與四次揮手的過程是怎樣的?
- HTTP 的封包格式又是怎樣的?
URL 解析
url 遵守的規則是這個樣子
scheme://host.domain:port/path/filename
每個名詞的含義如下解釋:
- scheme 定義應用層協定類型,比如 http、https、 ftp 等;
- host 定義域主機(http 的預設主機是 www);
- domain 定義網際網路域名,比如 segmentfault.com;
- port 主機的端口,http 預設是 80, https 預設是 443;
- path 伺服器上的資源路徑;
- filename - 定義文檔/資源的名稱;
DNS 查詢
浏覽器不能直接通過域名找到伺服器,隻能通過 IP 位址。
那浏覽器是如何通過域名查詢到我們輸入的 url 對應的 IP 呢?
- 浏覽器緩存:按照一定頻率緩存 DNS 資料。
- 作業系統緩存:如果浏覽器緩存找不到記錄則去作業系統中找。
- 路由緩存:路由器 DNS 緩存。
- ISP 的 DNS 伺服器:ISP 是網際網路服務提供商(Internet Service Provider)的簡稱,ISP 有專門的 DNS 伺服器應對 DNS 查詢請求。
- 根伺服器:ISP 的 DNS 伺服器還找不到的話,它就會向根伺服器送出請求,進行遞歸查詢(DNS 伺服器先問根域名伺服器.com 域名伺服器的 IP 位址,然後再問 .baidu 域名伺服器,依次類推)
TCP 連接配接建立與斷開
通過域名解析出 IP 位址以後就要建立 TCP/IP 連接配接了。
TCP/IP 分為四層,每一層都會加上一個頭部再發送給下一層。到了接收方後,對應的每一層則把對應層的頭部解析拆除,丢上上一層,跟發送端的過程反過來。
TCP/IP四層模型
應用層:發送 HTTP 請求
浏覽器從位址欄得到伺服器 IP,接着構造一個 HTTP 封包,其中包括:
- 請求行包含請求方法、URL、協定版本
- 請求報頭(Request Header):由 “關鍵字: 值”對組成,每行一對,關鍵字與值使用英文 “:” 分割
- 請求體:請求參數,并不是所有的請求都有請求參數。一般 get 參數 的格式 name=tom&password=1234&realName=tomson,也可以将參數放在 body 裡面。
傳輸層:TCP 傳輸封包
在傳輸封包之前會先建立 TCP/IP 連接配接,也就是後面我們要說的三次握手。
在這一層解決了資料可靠傳輸、及流量控制、擁塞控制。
可靠傳輸
對于發送方發送的資料,接收方在接受到資料之後必須要給予确認,确認它收到了資料。如果在規定時間内,沒有給予确認則意味着接收方沒有接受到資料,然後發送方對資料進行重發。
TCP的可靠傳輸是通過确認和逾時重傳的機制來實作的,而确認和逾時重傳的具體的實作是通過以位元組為機關的滑動視窗機制來完成。
TCP擁塞控制
TCP協定通過慢啟動機制、擁塞避免機制、加速遞減機制、快重傳和快恢複機制來共同實作擁塞控制。
流量控制
采用通知視窗實作對發送端的流量控制,通知視窗大小的機關是位元組。TCP通過在TCP資料段首部的視窗字段中填入目前設定的接收視窗(即通知視窗)的大小,用來告知對方 '我方目前的接收視窗大小',以實作流量控制。
通信雙方的發送視窗大小由雙方在連接配接建立的時候商定,在通信過程,雙方可以動态地根據自己的情況調整對方的發送視窗大小。
網絡層:IP 協定查詢 MAC 位址
将資料段打包,并加入源及目标的 IP 位址,并且負責尋找傳輸路線。判斷目标位址是否與目前位址處于同一網絡中,是的話直接根據 Mac 位址發送,否則使用路由表查找下一跳位址,以及使用 ARP 協定查詢它的 Mac 位址。
鍊路層:以太網協定
根據以太網協定将資料分為以“幀”為機關的資料包,每一幀分為兩個部分:
- 标頭:資料包的發送者、接受者、資料類型
- 資料:資料包具體内容
Mac 位址
以太網規定了連入網絡的所有裝置都必須具備“網卡”接口,資料包都是從一塊網卡傳遞到另一塊網卡,網卡的位址就是 Mac 位址。每一個 Mac 位址都是獨一無二的,具備了一對一的能力。
三次握手
在傳輸層傳輸資料之前需要建立連接配接,也就是三次握手建立可靠連接配接。
三次握手
首先建立連結前需要 Server 端先監聽端口,是以 Server 端建立連結前的初始狀态就是 LISTEN 狀态,這時 Client 端準備建立連結,先發送一個 SYN 同步包,發送完同步包後,Client 端的連結狀态變成了 SYN_SENT 狀态。Server 端收到 SYN 後,同意建立連結,會向 Client 端回複一個 ACK。
由于 TCP 是雙工傳輸,Server 端也會同時向 Client 端發送一個 SYN,申請 Server 向 Client 方向建立連結。發送完 ACK 和 SYN 後,Server 端的連結狀态就變成了 SYN_RCVD。
Client 收到 Server 的 ACK 後,Client 端的連結狀态就變成了 ESTABLISHED 狀态,同時,Client 向 Server 端發送 ACK,回複 Server 端的 SYN 請求。
Server 端收到 Client 端的 ACK 後,Server 端的連結狀态也就變成了的 ESTABLISHED 狀态,此時建連完成,雙方随時可以進行資料傳輸。
在面試時需要明白三次握手是為了建立雙向的連結,需要記住 Client 端和 Server 端的連結狀态變化。另外回答建連的問題時,可以提到 SYN 洪水攻擊發生的原因,就是 Server 端收到 Client 端的 SYN 請求後,發送了 ACK 和 SYN,但是 Client 端不進行回複,導緻 Server 端大量的連結處在 SYN_RCVD 狀态,進而影響其他正常請求的建連。可以設定 tcp_synack_retries = 0 加快半連結的回收速度,或者調大 tcp_max_syn_backlog 來應對少量的 SYN 洪水攻擊
四次揮手
我們隻要關注 80 端口與 13743 端口建立的連接配接斷開過程,浏覽器通過 13747 端口發送 [FIN, ACK] 這裡是不是跟很多網上看到的不一樣?
- 其實是用戶端在發送 [FIN] 封包的時候順帶發了一個 [ACK] 确認上次傳輸确認。
- 接着服務端通過 80 端口響應了 [ACK] ,然後立馬響應 [FIN, ACK] 表示資料傳輸完了,可以關閉連接配接。
- 最後浏覽器通過 13743 端口 發送 [ACK] 包給服務端,客服端與服務端連接配接就關閉了。
具體流程如下圖抓包所示:
三次握手與四次揮手
TCP 連接配接與斷開
用戶端:
- SYN_SENT - 用戶端發起第 1 次握手後,連接配接狀态為 SYN_SENT ,等待服務端核心進行應答,如果服務端來不及處理(例如服務端的 backlog 隊列已滿)就可以看到這種狀态的連接配接。
- ESTABLISHED - 表示連接配接處于正常狀态,可以進行資料傳送。用戶端收到伺服器回複的 SYN+ACK 後,對服務端的 SYN 單獨回複(第 3 次握手),連接配接建立完成,進入 ESTABLISHED 狀态。服務端程式收到第 3 次握手包後,也進入 ESTABLISHED 狀态。
- FIN_WAIT_1 - 用戶端發送了關閉連接配接的 FIN 封包後,等待服務端回複 ACK 确認。
- FIN_WAIT_2 - 表示我方已關閉連接配接,正在等待服務端關閉。用戶端發了關閉連接配接的 FIN 封包後,伺服器發回 ACK 應答,但是沒進行關閉,就會處于這種狀态。
- TIME_WAIT - 雙方都正常關閉連接配接後,用戶端會維持 TIME_WAIT 一段時間,以確定最後一個 ACK 能成功發送到伺服器端。停留時長為 2 倍的 MSL (封包最大生存時間),Linux 下大約是 60 秒。是以在一個頻繁建立短連接配接的伺服器上通常可以看到成千上萬的 TIME_WAIT 連接配接。
服務端:
- LISTEN - 表示目前程式正在監聽某個端口時。
- SYN_RCVD - 服務端收到第 1 次握手後,進入 SYN_RCVD 狀态,并回複一個 SYN+ACK(第 2 次握手),再等待對方确認。
- ESTABLISHED - 表示連接配接處于正常狀态,可以進行資料傳送。完成 TCP3 次握手後,連接配接建立完成,進入 ESTABLISHED 狀态。
- CLOSE_WAIT - 表示用戶端已經關閉連接配接,但是本地還沒關閉,正在等待本地關閉。有時用戶端程式已經退出了,但服務端程式由于異常或 BUG 沒有調用 close()函數對連接配接進行關閉,那在伺服器這個連接配接就會一直處于 CLOSE_WAIT 狀态,而在客戶機已經不存在這個連接配接了。
- LAST_ACK - 表示正在等待用戶端對服務端的關閉請求進行最終确認。
TIME_WAIT 狀态存在的理由:
劃重點了
- 可靠地實作 TCP 全雙工連接配接的終止 在進行關閉連接配接四路握手協定時,最後的 ACK 是由主動關閉端發出的,如果這個最終的 ACK 丢失,伺服器将重發最終的 FIN,是以用戶端必須維護狀态資訊允 許它重發最終的 ACK。如 果不維持這個狀态資訊,那麼用戶端将響應 RST 分節,伺服器将此分節解釋成一個錯誤( 在 java 中會抛出 connection reset 的 SocketException)。因而,要實作 TCP 全雙工連接配接的正常終 止,必須處理終止序列四個分節中任何一個分節的丢失情況,主動關閉 的用戶端必須維持狀 态資訊進入 TIME_WAIT 狀态。
- 允許老的重複分節在網絡中消逝 TCP 分節可能由于路由器異常而“迷途”,在迷途期間,TCP 發送端可能因确認逾時而重發這個 分節,迷途的分節在路由器修複後也會被送到最終目的地,這個 原來的迷途分節就稱為 lost duplicate。在關閉一個 TCP 連接配接後,馬上又重建立立起一個相同的 IP 位址和端口之間的 TCP 連接配接,後一個連接配接被稱為前一個連接配接的化身 ( incarnation),那麼有可能出現這種情況,前一 個連接配接的迷途重複分組在前一個連接配接終止後出現,進而被誤解成從屬于新的化身。為了避免 這個情 況,TCP 不允許處于 TIME_WAIT 狀态的連接配接啟動一個新的化身,因為 TIME_WAIT 狀 态持續 2MSL,就可以保證當成功建立一個 TCP 連接配接的時 候,來自連接配接先前化身的重複分組已 經在網絡中消逝。
另外回答斷鍊的問題時,可以提到實際應用中有可能遇到大量 Socket 處在 TIME_WAIT 或者 CLOSE_WAIT 狀态的問題。一般開啟 tcp_tw_reuse 和 tcp_tw_recycle 能夠加快 TIME-WAIT 的 Sockets 回收;而大量 CLOSE_WAIT 可能是被動關閉的一方存在代碼 bug,沒有正确關閉連結導緻的。
簡單地說就是
- 保證 TCP 協定的全雙工連接配接能夠可靠關閉;
- 保證這次連接配接的重複資料段從網絡中消失,防止端口被重用時可能産生資料混淆;
點選下方,第一時間了解華為雲新鮮技術~
華為雲部落格_大資料部落格_AI部落格_雲計算部落格_開發者中心-華為雲