這裡是《寫給前端工程師的 HTTP 系列》,記得有位大佬曾經說過:“大廠前端面試對 HTTP 的要求比 CSS 還要高”,由此可見 HTTP 的重要程度不可小視。文章寫作計劃如下,視情況可能有一定的刪減,本篇是該系列的第 1 篇 —— 《從 TCP/UDP 到 DNS 解析》。
更多文章可關注我的 interview 系列。
寫作計劃
-
Web 伺服器 (nginx/caddy)
-
HTTPS (對稱加密/非對稱加密/SSL)
-
JWT (我自己部落格的後臺用到了,所以專門要寫一篇文章)
-
SPDY / HTTP/2 / Websockets
-
網路攻擊
-
跨域
-
快取機制
-
瀏覽器原理
-
終章:從輸入 url 到頁面呈現發生了什麼
HTTP 的發展歷程
HTTP/0.9
標準於 1990 年問世,因為當時的 HTTP 沒有作為正式的標準被確立,該版本含有 HTTP/1.0 之前版本的意味。
HTTP/1.0
標準於 1996 年 5 月作為第一份標準被公佈,它被記載於 RFC1945 - Hypertext Transfer Protocol -- HTTP/1.0
HTTP/1.1
標準於 1999 年 6 月被公佈,截止到目前它應該是最主流的 HTTP 協議版本,它被記載於 RFC2616 - Hypertext Transfer Protocol -- HTTP/1.1
HTTP/2
標準於 2015 年 5 月被正式釋出,它被記載於 RFC7540 - Hypertext Transfer Protocol -- HTTP/2,它的特點是 ① 採用二進位制而非明文來打包,② 多路複用,③ 修復隊頭堵塞,④ 允許設定設定請求優先順序,⑤ 伺服器推送,⑥ WebSocket 等等。
據 w3techs 統計,截止到 2019/04/22,HTTP/2 的全球佔有率為 36%。我的 個人部落格 在上線之初就支援了 HTTP/2。
TCP/IP 通訊傳輸流
TCP/IP 五層協議
在講解 TCP/IP 通訊傳輸流之前,首先複習一下 TCP/IP 的五層協議。
應用層:決定向使用者提供應用服務時通訊的活動。TCP/IP 協議族內預存了各類通用的應用服務。比如:FTP、DNS、HTTP 協議。
傳輸層:傳輸層對上層應用層,提供處於網路連線中的兩臺計算機之間的資料傳輸。在傳輸層有兩個性質不同的協議,分別是 TCP (Transmission Control Protocol,傳輸控制協議) 和 UDP (User Data Protocol,使用者資料包協議)
網路層:網路層用來處理在網路上流動的資料包。資料包是網路傳輸的最小資料單位。該層規定了通過怎樣的路徑到達對方計算機,並把資料包傳送給對方。與對方計算機通過多臺計算機或網路裝置進行傳輸時,網路層所起的作用就是在眾多的選項內選擇一條傳輸路線。
資料鏈路層: 在物理層提供位元流服務的基礎上,建立相鄰結點之間的資料鏈路,通過差錯控制提供資料幀 (Frame)在通道上無差錯的傳輸,並進行各電路上的動作系列。資料的單位稱為幀 (frame)
物理層:物理層建立在物理通訊介質的基礎上,作為系統和通訊介質的介面,用來實現資料鏈路實體間透明的位元 (bit) 流傳輸。只有該層為真實物理通訊,其它各層為虛擬通訊。
TCP/IP 資料傳輸流程
客戶端在應用層 (HTTP 協議) 發出一個 HTTP 請求。
為了方便傳輸,在傳輸層 (TCP 協議) 把從應用層處收到的資料 (HTTP 請求報文) 進行分割,並在各個報文上打上標記序號及埠號後轉發給網路層。
在網路層 (IP 協議),增加作為通訊目的地的 MAC 地址後轉發給資料鏈路層。這樣,傳送給服務端的請求就準備齊全了。
當服務端在鏈路層接收到資料時,按序往上層傳送,一直到應用層。當傳輸到應用層時,才算真正的接收到由客戶端傳送過來的請求。
什麼是 MAC 地址?
媒體訪問控制地址 (Media Access Control Address),也稱為區域網地址 (LAN Address),乙太網地址 (Ethernet Address) 或實體地址 (Physical Address),它是一個用來確認網上裝置位置的地址。ARP (Address Resolution Protocol) 是一種用來解析地址的協議,它可以根據 IP 地址反查出對應的 MAC 地址。
下圖展示了一臺電腦內網 IP 和 MAC 地址。在終端 (MAC OS 環境) 輸入 ifconfig
,找到 en0
,便可查詢本地乙太網的資訊。
那麼什麼是 MAC 地址呢?我們知道 IP 地址是可變的,可以通過各種方式分配 IP 地址給一個裝置,比如 DHCP, PPP,靜態 IP 等。而 MAC 地址一般來講是不會變的,裝置在生產時就被“烙”上了 唯一的標識
,這個 唯一的標識
就是 MAC 地址。
逼乎上有個很有趣的例子:你中午在公司點了份外賣,收貨地址一定是寫公司的地址;晚上回到家,再點外賣時就得把地址寫成家 (IP 是動態的)。但無論在哪兒點外賣,訂單上的姓名和手機號一定是你自己的 (MAC 地址)。
中午外賣小哥把午餐送到公司門口,但收外賣的人肯定不止你一個 (多臺裝置在同一個 broadcast 網路裡),因此他會通過手機號和姓名來找到你。
UDP 協議
使用者資料包協議 (User Datagram Protocol),又稱使用者資料包協議,是一個簡單的面向資料包的傳輸協議。在 TCP/IP 模型中,UDP 為網路層以上和應用層以下提供了一個簡單的介面。UDP 只提供資料的 不可靠傳遞
,它一旦把應用程式發給網路層的資料傳送出去,就不保留資料備份。UDP 在 IP 資料包的頭部僅僅加入了複用和資料校驗 (欄位)。
它的特點如下:
-
UDP 缺乏可靠性。UDP 本身不提供確認序號,序列號,超時重傳等機制。UDP 資料包可能在網路中被複制,被重新排序。即 UDP 不保證資料包會到達其最終目的地,也不保證各個資料包的先後順序,也不保證每個資料包只到達一次。
-
UDP 頭部開銷小,它包含以下幾個資料:
-
兩個十六位的埠號,分別是源埠和目的埠。
-
整個資料包文的長度。
-
整個資料包文的校驗和,用於發現頭部資訊和資料中的錯誤
-
-
UDP 是面向無連線的。UDP 客戶端和伺服器之前不必存在長期的關係。UDP 傳送資料包之前也不需要經過握手建立連線的過程。
-
UDP 不僅支援單播,還支援多播和廣播。
基於 UDP 協議的有:
-
域名系統 (DNS)
-
簡單網路管理協議 (SNMP)
-
動態主機配置協議 (DHCP)
-
路由資訊協議 (RIP)
-
自舉協議 (BOOTP)
-
簡單檔案傳輸協議 (TFTP)
TCP 協議
TCP (Transmission Control Protocol, 傳輸控制協議) 是一種面向連線的、可靠的、基於位元組流服務的傳輸層通訊協議,由 IETF 的 RFC 793 定義。其中位元組流服務 (Byte Stream Service) 是指為了方便傳輸,將大塊資料分割成以報文段 (segment) 為單位的資料包進行管理。
-
TCP 提供一種面向連線的、可靠的位元組流服務
-
在一個 TCP 連線中,僅有兩方進行彼此通訊。廣播和多播不能用於 TCP
-
TCP 使用校驗和,確認和重傳機制來保證可靠傳輸
-
TCP 給資料分節進行排序,並使用累積確認保證資料的順序不變和非重複
-
TCP 使用滑動視窗機制來實現流量控制,通過動態改變視窗的大小進行擁塞控制
TCP 報文
埠號:包括源埠號和目的埠號,用來標識同一臺計算機的不同的應用程式。TCP 報頭中的源埠號和目的埠號同 IP 資料包中的源 IP 與目的 IP 唯一確定一條 TCP 連線。
-
源埠號:源埠和 IP 地址的作用是標識報文的返回地址。
-
目的埠號:目的埠指明接收方計算機上的應用程式介面。
序號:它是當前報文段傳送的資料組的第一個位元組的序號。在 TCP 傳送的流中,每一個位元組一個序號。比如一個報文段的序號為 300,此報文段的資料部分共有 100 位元組,則下一個報文段的序號為 400。序號 確保了 TCP 傳輸的有序性。
確認號:即 ACK(acknowledgement),指明下一個期待收到的位元組序號,表明該序號之前的所有資料已經正確無誤的收到。確認號只有當 ACK 標誌為 1 時才有效。比如建立連線時,SYN 報文的 ACK 標誌位為 0。
首部長度:由於首部可能含有可選項內容,因此 TCP 報頭的長度是不確定的,報頭不包含任何任選欄位則長度為 20 位元組,4 位首部長度欄位所能表示的最大值為 1111,轉化為 10 進製為 15,15*32/8 = 60,故報頭最大長度為 60 位元組。首部長度也叫資料偏移,是因為首部長度實際上指示了資料區在報文段中的起始偏移值。
保留:為將來定義新的用途保留,現在一般置 0。
控制位 | 說明 |
---|---|
URG | 緊急指標標誌,為 1 時表示緊急指標有效,為 0 則忽略緊急指標。 |
ACK (acknowledgement) | 確認序號標誌,為 1 時表示確認號有效,為 0 表示報文中不含確認資訊,忽略確認號欄位。 |
PSH | push 標誌,為 1 表示是帶有 push 標誌的資料,指示接收方在接收到該報文段以後,應儘快將這個報文段交給應用程式,而不是在緩衝區排隊。 |
RST | 重置連線標誌,用於重置由於主機崩潰或其他原因而出現錯誤的連線。或者用於拒絕非法的報文段和拒絕連線請求。 |
SYN (synchronize) | 同步序號,用於建立連線過程,在連線請求中,SYN=1 和 ACK=0 表示該資料段沒有使用捎帶的確認域,而連線應答捎帶一個確認,即 SYN=1 和 ACK=1。 |
FIN (Finish) | finish 標誌,用於釋放連線,為 1 時表示傳送方已經沒有資料傳送了,即關閉本方資料流。 |
視窗:滑動視窗大小,用來告知傳送端接受端的快取大小,以此控制傳送端傳送資料的速率,從而達到流量控制。視窗大小是一個 16bit 欄位,因此視窗大小最大為 65535。
校驗和:奇偶校驗,此校驗和是對整個的 TCP 報文段,包括 TCP 頭部和 TCP 資料,以 16 位字進行計算所得。由傳送端計算和儲存,並由接收端進行驗證。
緊急指標:只有當 URG 標誌置 1 時緊急指標才有效。緊急指標是一個正的偏移量,和順序號欄位中的值相加表示緊急資料最後一個位元組的序號。 TCP 的緊急方式是傳送端向另一端傳送緊急資料的一種方式。
選項和填充:最常見的可選欄位是最長報文大小,又稱為 MSS (Maximum Segment Size),每個連線方通常都在通訊的第一個報文段 (為建立連線而設定 SYN 標誌為 1 的那個段)中指明這個選項,它表示本端所能接受的最大報文段的長度。選項長度不一定是 32 位的整數倍,所以要加填充位,即在這個欄位中加入額外的零,以保證 TCP 頭是 32 的整數倍。
資料部分:TCP 報文段中的資料部分是可選的。在一個連線建立和一個連線終止時,雙方交換的報文段僅有 TCP 首部。如果一方沒有資料要傳送,也使用沒有任何資料的首部來確認收到的資料。在處理超時的許多情況中,也會傳送不帶任何資料的報文段。
TCP 建立連線 (三次握手)
- 我可以連你嘛?
- 可以。
- 那我連了。
emmmmm,單身久了,看三次握手都那麼眉清目秀。
所謂三次握手 (three-way handshaking) 是指建立一個 TCP 連線時,需要客戶端和服務端共傳送三個包。它的目的是連線伺服器指定埠,建立 TCP 連線,並同步雙方的 序列號
和 確認號
,交換 TCP 視窗大小資訊。在 socket 程式設計中,當客戶端執行 connect() 函式時,將觸發三次握手。
-
第一次握手
客戶端首先傳送一個 SYN 為 1 的包給服務端,指明客戶端要連線服務端的哪個介面以及初始序號 x。傳送完畢後,客戶端進入
SYN_SEND
狀態。 -
第二次握手
服務端收到後,回傳一個帶有 SYN/ACK 的確認包以示應答。即 SYN=1,ACK=1。服務端選擇自己的 ISN 序列號,放到 seq 中,同時將確認序號 ack 設定為客戶端的 ISN+1,即 x+1。傳送完畢後,服務端進入
SYN_RCVD (同步收到)
狀態。 -
第三次握手
客戶端收到確認後,再次傳送一個帶 ACK 標誌的資料包。即 ACK=1,ack=y+1,並將自己的序列號 seq=x+1。傳送完畢後,客戶端和伺服器雙雙進入
ESTABLISHED
狀態。至此,三次握手結束。
TCP 關閉連線 (四次揮手)
- 客戶端:我要睡了
- 服務端:嗯,睡吧,晚安
- 服務端:我也要睡了
- 客戶端:晚安,好夢
TAT,好虐。
-
第一次揮手
客戶端呼叫 close() 函式,併傳送一個 FIN (finish) 標誌為 1 的資料包給服務端,來表示本方的資料已經全部傳送完畢,此時客戶端進入
FIN_WAIT_1
狀態。TCP 規定,FIN 報文段即使不攜帶資料,也要消耗一個序號。 -
第二次揮手
服務端收到客戶端的釋放報文後,發出確認報文,其中 ACK=1, ack=u+1,並且帶上自己的序列號 seq=v。表明自己接受到了客戶端關閉連線的請求,但還沒準備好關閉連線 (半關閉狀態),也就是說客戶端已經沒有資料要傳送了,但服務端仍有可能會傳送資料。傳送完畢後,服務端進入
CLOSE_WAIT
狀態。當客戶端收到該報文後,客戶端就進入
FIN-WAIT-2
狀態,等待伺服器傳送連線釋放報文。 -
第三次揮手
伺服器端準備好關閉連線時,會向客戶端傳送一個連線釋放報文,其中 FIN=1,ack=u+1。由於在半關閉狀態,伺服器很可能又傳送了一些資料,假定此時的序列號為 seq=w。傳送完畢後,服務端便進入
LAST-ACK
(最後確認) 狀態。 -
第四次揮手
客戶端收到伺服器的連線釋放報文後,需要傳送一個確認包,其中 ACK=1,ack=w+1,而自己的序列號是 seq=u+1,並進入了
TIME-WAIT
(時間等待)狀態,等待過程可能出現的要求重傳的 ACK 包。此時 TCP 連線還沒有釋放,必須經過 2 * MSL (Maximum Segment Lifetime, 最長報文段壽命) 時間後,當客戶端撤銷相應的 TCB 後,才會進入
CLOSED
狀態。而伺服器只要收到了客戶端發出的確認,立即進入
CLOSED
狀態。同樣,撤銷 TCB 後,就結束了這次的 TCP 連線。因此,伺服器結束 TCP 連線的時間要比客戶端早一些。
TCP 和 UDP 的對比
UDP | TCP | |
---|---|---|
是否連線 | 無連線 | 面向連線 |
是否可靠 | 不可靠傳輸,不使用流量控制和擁塞控制 | 可靠傳輸,使用流量控制和擁塞控制 |
連線物件個數 | 支援一對一,一對多,多對一和多對多互動通訊 | 只能是一對一通訊 |
傳輸方式 | 面向報文 | 面向位元組流 |
首部開銷 | 首部開銷小,僅 8 位元組 | 首部最小 20 位元組,最大 60 位元組 |
適用場景 | 適用於實時應用 (IP 電話、視訊會議、直播等) | 適用於要求可靠傳輸的應用,例如檔案傳輸 |
DNS
DNS 報文格式
請求報文和 DNS 伺服器返回的應答報文都是統一的格式:
-
會話標識 (2 位元組):它是 DNS 報文的 ID 標識,對於請求報文和其對應的應答報文,這個欄位是相同的,通過它可以區分 DNS 應答報文是哪個請求的響應。
-
標誌 (2 位元組):它有 8 個部分,如下圖所示:
欄位 說明 QR (1bit) 查詢/響應標誌,0 為查詢報文,1 為響應報文 opcode (4bit) 0 表示標準查詢,1 表示反向查詢,2 表示伺服器狀態請求,3-15 是保留值 AA (1bit) 表示授權回答,該欄位在應答的時候才有意義,指出給出應答的伺服器是查詢域名的授權解析伺服器; TC (1bit) 表示可截斷的,用來指出報文比允許的長度還要長,導致被截斷 RD (1bit) 表示期望遞迴,該欄位被請求設定,應答的時候使用的相同的值返回。如果設定了 RD,就建議域名伺服器進行遞迴解析,遞迴查詢的支援是可選的 RA (1bit) 表示可用遞迴,該欄位在應答中設定或取消,用來代表伺服器是否支援遞迴查詢 RCODE (4bit) 應答碼,0 表示沒有差錯,3 表示名字差錯,2 表示伺服器錯誤 Z 保留值 -
Questions 查詢欄位
-
QNAME 無符號 8bit 為單位長度不限表示查詢名。
-
QTYPE 無符號 16bit 整數表示查詢的協議型別。
-
QCLASS 無符號 16bit 整數表示查詢的類。
-
-
Answer/Authority/Additional
三者的格式相同,如下所示:
-
NAME 資源記錄包含的域名。
-
TYPE 表示 DNS 協議的型別。
-
CLASS 表示 RDATA 的類。
-
TTL 表示資源記錄可以快取的時間。0 代表只能被傳輸,但是不能被快取。
-
RDLENGTH 表示 RDATA 的長度。
-
RDATA 不定長字串來表示記錄,格式根 TYPE 和 CLASS 有關。比如,TYPE 是 A,CLASS 是 IN,那麼 RDATA 就是一個 4 個位元組的 ARPA 網路地址。
-
DNS 解析記錄
折騰過搭建網站的小夥伴們一定對 DNS 解析記錄不會陌生,下面通過表格複習一下。
型別 | 助記詞 | 說明 |
---|---|---|
1 | A | 由域名獲得 IPv4 地址 (常用) |
2 | NS | 查詢域名伺服器 (常用) |
5 | CNAME | 設定域名別名 (常用) |
6 | SOA | 開始授權 |
11 | WKS | 熟知服務 |
12 | PTR | 把 IP 地址轉換成域名 |
13 | HINFO | 主機資訊 |
15 | MX | 郵件交換 (常用) |
28 | AAAA | 由域名獲得 IPv6 地址 (常用) |
252 | AXFR | 傳送整個區的請求 |
255 | ANY | 對所用記錄的請求 |
域名解析過程
-
系統會檢查瀏覽器快取中有沒有這個域名對應的解析過的 IP 地址,如果快取中有,這個解析過程就將結束。瀏覽器快取是受這個域名的失效時間和快取的空間大小控制的。
-
如果使用者的瀏覽器快取中沒有,瀏覽器會查詢作業系統快取中即為本地的 Host 檔案。
-
路由器也可能會有快取。
-
如果前幾步都沒有找到,就會到 LDNS (Local DNS) 中查詢,LDNS 是你的 ISP 分配給你的 DNS (一般為兩個),大部分情況下域名都會在這裡得到解析。下圖是 cloudflare 提供給我的兩個 DNS。
-
如果在 LDNS 沒有找到,那就要去 Root Server 域名伺服器請求解析了。根域名伺服器返回給本地域名伺服器一個
查詢主域名伺服器(gTLD Server)地址
。gTLD 是國際頂級域名伺服器,如.com,.cn、.org 等,全球只有 13 臺左右。 -
本地域名伺服器會向 gTLD Server 地址傳送請求,它會返回一個 Name Server 域名伺服器的地址,這個 Name Server 通常就是你註冊域名的廠家,比如 NameCheap、狗爹、萬網等等。
-
Name Server 域名伺服器會查詢儲存域名和 IP 的關係對映表,正常情況下都可以根據域名得到目標 IP 記錄,並連同一個 TTL 返回給本地域名伺服器。
-
本地域名伺服器根據 TTL 快取這個 IP,並將解析結果返回給客戶端,客戶端再根據 TTL 將 IP 資訊快取到本地系統快取裡。至此,域名解析過程結束。
淺談 CDN
CDN 全稱為內容分發網路 (Content Delivery Network),它能夠實時地根據網路流量和各節點的連線、負載狀況以及到使用者的距離和響應時間等綜合資訊將使用者的請求重新導向離使用者最近的服務節點上,以提高使用者訪問網站的相應速度。
通俗來講,原本使用者訪問的資源是存放在你自己的伺服器,而現在訪問的資源來自 CDN 快取伺服器。在實際操作中,我們只需要將域名的 DNS 解析指向 CDN 服務商提供的域名伺服器即可。
我一直在用 cloudflare 的免費版,很贊。它提供了 HTTP/2,IPV6,brotli (一種壓縮演算法,比 gzip 壓縮率還要高的多)。
典型的 CDN 系統由下面兩個部分組成:
- 分發服務系統
分發服務系統的基元是 Cache 裝置,它會同步源站點的內容並負責響應使用者的訪問請求,把快取在本地的內容快速的提供給使用者。
- 負載均衡系統
對發起請求的使用者進行訪問排程,確定提供給使用者的最終實際訪問地址。該系統分為全域性負載均衡 (GSLB) 和本地負載均衡 (SLB)。GBLB 主要根據“就近原則”,通過對每個服務節點進行最優判斷,向使用者提供最合適的 Cache 裝置。SLB 主要負責節點內部的裝置負載均衡。
幾道面試題
三次握手和四次揮手詳細介紹 (重點)
見上文。
TCP 有哪些手段保證可靠交付
-
為了方便傳輸,TCP 協議將大塊資料分割成以報文段為單位的資料塊進行管理。
-
當 TCP 發出一個報文段時,它會啟動一個定時器,等待目的端確認收到這個報文段。如果不能及時收到一個確認,將重發這個報文段。
-
當收到來自另一端的資料時,它會傳送一個確認,但該確認不是立即傳送的,之所以推遲,是要對包做完整校驗。
-
TCP 通過檢驗
校驗和
的方式來檢測資料的準確性,當檢測到資料出錯後,會丟給客戶端一個攜帶 NCK 標誌的包,當客戶端收到後會重現傳送一遍資料包。 -
TCP 協議給每一個位元組設定一個序號,序號確保了 TCP 傳輸的有序性,當報文段出現失序的問題,TCP 會根據序號重新排序。
-
IP 資料包有可能會發生重複,TCP 接收端會丟棄重複的資料。
-
TCP 提供流量控制。TCP 連線的每一方都有固定大小的緩衝空間,TCP 的接收端只允許另一端傳送接收端緩衝區所能接納的資料。TCP 使用的流量控制協議是可變大小的滑動視窗協議。
為什麼是三次握手,兩次不行嗎?
為了實現可靠資料傳輸,TCP 協議的通訊雙方,都必須維護一個序列號,以標識傳送出去的資料包中,哪些是已經被對方收到的。三次握手的過程即是通訊雙方相互告知序列號起始值,並確認對方已經收到了序列號起始值的必經步驟
如果只是兩次握手,至多隻有連線發起方的起始序列號能被確認,另一方選擇的序列號則得不到確認。
如果已經建立了連線,但是客戶端突然出現故障了怎麼辦?
TCP 還設有一個保活計時器 (keep-alive),如果客戶端出現故障,伺服器不能一直等下去,白白浪費資源。伺服器每收到一次客戶端的請求後都會重新復位這個計時器,時間通常是設定為 2 小時,若兩小時還沒有收到客戶端的任何資料,伺服器就會傳送一個探測報文段,以後每隔 75 秒傳送一次。若一連傳送 10 個探測報文仍然沒反應,伺服器就認為客戶端出了故障,接著就關閉連線。
DNS 解析流程 (重點)
見上文。
最後
下一篇將對 HTTP 協議進行全面剖析,敬請期待。
歡迎關注我的微信公眾號:進擊的前端
參考
《圖解 HTTP》 -- 上野宣
[面試 ∙ 網路] TCP/IP (四):TCP 與 UDP 協議簡介