Stories

Detail Return Return

【面試系列】萬字長文,速通TCP、HTTP(s)、DNS、CDN、websocket、SSE - Stories Detail

引言:
本文用通俗簡練的語言又不失細節(自認為doge)地介紹TCP/UDP、HTTP、HTTPs、DNS、CDN、Websocket和SSE等。本文面向求職面試人羣,比較全面的歸納了面試中計算機網絡涵蓋的面試點,你可以結合本文自行拓展深度和廣度。如果你準備時間不夠,更加推薦你看這篇文章!
如果錯誤或侵權之處歡迎指正和聯繫我。

一、OSI 七層模型

計算機網絡的7層模型,也稱為OSI七層參考模型,是一個概念框架,它將網絡通信功能劃分為七個抽象層,從下到上分別是:物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。
在實際應用中,通常抽象為5層模型,如下圖所示:
image.png

二、TCP和UDP

TCP和UDP的區別

1.總體

  • TCP是基於連接的可靠的傳輸協議
  • UDP是不基於連接的不可靠傳輸協議

2.報文頭

  • TCP報文頭20個字節,除了端口、校驗和字段,還有seqNum, ackNum,標記位和窗口大小等。
  • UDP報文頭8個字節,只有端口、包長度和校驗和字段。

(圖片來自TCP 頭格式有哪些?)
image.png

image.png

3.可靠性

  • TCP 擁塞控制、流量控制、重傳機制、滑動窗口等機制保證傳輸質量
  • UDP沒有

4.雙工性

  • TCP 是點對點通信 全雙工通信
  • UDP 支持一對一、一對多、多對一和多對多的交互通信。

TCP 三次握手

流程

(圖片來自三次握手過程是怎麼樣的?)

image.png

一次握手
客户端會隨機初始化序號(client_isn),將此序號置於 TCP 首部的「序列號」字段中,同時把 SYN 標誌位置為 1 ,表示 SYN 報文。接着把第一個 SYN 報文發送給服務端,表示向服務端發起連接,該報文不包含應用層數據,之後客户端處於 SYN-SENT 狀態。

二次握手
服務端收到客户端的 SYN 報文後,首先服務端也隨機初始化自己的序號(server_isn),將此序號填入 TCP 首部的「序號」字段中,其次把 TCP 首部的「確認應答號」字段填入 client_isn + 1, 接着把 SYNACK 標誌位置為 1。最後把該報文發給客户端,該報文也不包含應用層數據,之後服務端處於 SYN-RCVD 狀態。

三次握手
客户端收到服務端報文後,還要向服務端迴應最後一個應答報文,首先該應答報文 TCP 首部 ACK 標誌位置為 1 ,其次「確認應答號」字段填入 server_isn + 1 ,最後把報文發送給服務端,這次報文可以攜帶客户到服務器的數據,之後客户端處於 ESTABLISHED 狀態。

建議自己可以按下面的方式寫寫記記

1)Client -> Server:
SYN=1, SeqNum=x (client_state='syn_sent')
2)Server -> Client:
SYN=1, ACK=1, SeqNum=y, AckNum=x+1 (server_state='syn_recevied')
3)Client -> Server:
ACK=1, AckNum=y+1 (client_state='established',當Server收到Client的確認應答後server_state='established')
問題1:為什麼2次不行?

a.阻止重複的歷史連接,避免資源浪費
假設client發送了兩次SYN報文(舊SYN報文,新SYN報文)。若舊報文先到,Server收到後發出ACK報文的AckNum和Client預期的不一樣(不是新SeqNum+1), Client就會發送一個RST報文取消連接。若新報文先到,Client發送一個ACK報文告訴Server可以建立連接。
由上可以看出3次握手可以阻止歷史連接,否則的話Server會先建立連接,客户端沒有建立連接,這樣造成Server的資源浪費

b.同步雙方初始序列號。
Client發送SYN報文後得到來自Server的ACK報文,説明服務端收到了客户端的序列號;Server發送SYN後得到來自Client的ACK,説明客户端收到了服務端的序列號;這樣一來一回,才能保證雙方的初始序列號能被可靠的同步

c.3次可以建立可靠連接,就不需要4次

問題2:序列號的作用以及是如何生成的?

序列號能保證數據包不重複、不丟失和按序傳輸。
生成算法:ISN=M+F。M是時鐘,每4ms +1。F是對(源IP,目標IP,源端口,目標端口)的hash值。
如果序列號相同,舊連接的歷史報文殘留在網絡中,接受的一方無法分辨。對於1個TCP連接,序列號的生成大概4個小時就會重複,1個TCP連接一般不會這麼久。

問題3:第三次可以攜帶數據嗎?

可以

TCP四次揮手

流程

(圖片來自TCP 四次揮手過程是怎樣的?)image.png

一次揮手
客户端打算關閉連接,此時會發送一個 TCP 首部 FIN 標誌位被置為 1 的報文,也即 FIN 報文,之後客户端進入 FIN_WAIT_1 狀態。

二次揮手
服務端收到該報文後,就向客户端發送 ACK 應答報文,接着服務端進入 CLOSED_WAIT 狀態。

三次揮手
客户端收到服務端的 ACK 應答報文後,之後進入 FIN_WAIT_2 狀態。等待服務端處理完數據後,也向客户端發送 FIN 報文,之後服務端進入 LAST_ACK 狀態。

四次揮手

  • 客户端收到服務端的 FIN 報文後,回一個 ACK 應答報文,之後進入 TIME_WAIT 狀態
  • 服務器收到了 ACK 應答報文後,就進入了 CLOSED 狀態,至此服務端已經完成連接的關閉。
  • 客户端在經過 2MSL 一段時間後,自動進入 CLOSED 狀態,至此客户端也完成連接的關閉。

建議自己可以按下面的方式寫寫記記

1)client -> server: FIN
2)server -> client: ACK
3)server -> client:FIN (此時數據不能再傳輸)
4)client -> server:ACK (server 關閉)
5)client: 等2MSL (Maximum Segment LifeTime 最大報文存活時間) 後關閉

也可以動手自己畫畫這個流程圖,像下面這樣
image.png

為什麼兩次不行?

如果server 收到FIN報文就中斷,那麼server還有數據要傳輸怎麼辦? 所以為了能把剩下的數據傳完,2次不行。
另外,client發出FIN報文期間,可能還有數據在網絡中傳輸,未到server。如果FIN先到了,server關閉,則數據丟失。

為什麼三次不行?

情況一: 第二次握手和第三次握手合併
在server沒有數據傳輸的情況下,是可能合併的,即第二次握手即是FIN報文又是ACK報文。

情況二: 不需要第四次握手
不行。這種情況下server一發出FIN報文就close,無法判斷是否丟包-> 無法判斷client是否關閉。

為什麼要等2MSL?

Client收到FIN發送ACK後進入一個TIME_WAIT ,在TIME_WAIT的期間內如果沒有繼續收到Server發的FIN説明服務端已經關閉連接(否則服務端會重發FIN)。
從而判斷出server已經關閉 -> client可以關閉連接了。

非正常關閉

TCP Keepalive機制是TCP提供的一種檢測連接是否存活的可選機制,主要用於:

  1. 檢測對端是否仍然可達
  2. 防止中間設備因超時斷開連接
  3. 釋放已失效連接佔用的資源

Keepalive工作原理

  1. ​空閒檢測​​:當連接空閒時間超過tcp_keepalive_time(默認2小時)後,開始發送探測包
  2. ​探測間隔​​:每隔tcp_keepalive_intvl(默認75秒)發送一次探測包
  3. ​重試次數​​:最多嘗試tcp_keepalive_probes(默認9次)次
  4. ​判定失效​​:如果所有探測都無響應,則認為連接已斷開

基於Keepalive機制,如果TCP連接的一方非正常關閉後,另一方會在一定時機發送探測報文來確定對方是否還“活着”。如果對方已關閉,則關閉己方的TCP連接,釋放資源。

TCP 可靠性傳輸

總的來説,TCP通過序列號機制、校驗和、重傳機制、流量控制和擁塞控制保證了TCP。

序列號機制

通過Sequence Number和ACK Number給發送數據編號,通信過程中接收方就能知道那個包丟了,那個包接受了。

校驗和

TCP包的偽首部有一個校驗和字段,能對TCP的首部和數據進行校驗,判斷數據是否在傳輸時出錯了。

重傳機制

超時重傳
超過一定時間,説明包丟失了,重傳(這個"一定時間"是採樣得到的,大於一個往返時延)。

快速重傳
連續收到三個重複的ACK報文、就立即重傳。根據ACK Number立即重傳包,不等待超時機制觸發了。

SACK機制
前面有個一個報文丟失了,收到重複的ACK number,快速重新哪些報文呢?
所以ACK報文還要告訴對方ACK number之後哪些報文已接受了,這樣發送方對於已經接受到的報文就不發了(不是一股腦從ACK number序號後全發)。

流量控制

定義:針對接收方的接受速度,調整發送速度,減少包丟失。
TCP的頭部有一個Window字段。接收方在響應報文中設置Window字段可以調整發送方的 send window大小。

擁塞控制

定義:為了避免網絡擁塞,導致丟包甚至網絡癱瘓問題,從而設計一些算法來控制發送端的速度。

有四個算法:慢啓動算法避免擁塞算法快速恢復擁塞算法
1)剛建立連接:「慢啓動算法」(擁塞窗口大小cwnd) 指數增長
2)增長到閾值時:「避免擁塞算法」開始線性增長
3)出現快速重傳:「快速恢復算法」cwnd為原來的一半+3,閾值為原來cwnd的一半。
4)出現超時重傳:「擁塞算法」cwnd減半,閾值為原來的cwnd的一半。

P.S. swnd = min(cwnd, rwnd)

  • swnd: send window;
  • cwnd: crowd window;
  • rwnd: receive window;

(圖片來自「小林coding」)
image.png

【參考】

TCP 三次握手與四次揮手面試題——小林coding
TCP 重傳、滑動窗口、流量控制、擁塞控制——小林coding

三、HTTP

HTTP 1.1與1.0的區別

區別

  • 長連接:HTTP 1.1 默認使用持久連接(Connection: keep-alive),同一 TCP 連接可複用多個請求/響應;HTTP/1.0 默認每次請求都新建連接(最重要的!)。
  • 更完善的緩存機制:Cache-ControlETag/If-None-Match 等,更準確和精細化控制緩存與節省帶寬。
  • 升級與代理:Connection:UpgradeVia 等,明確協議升級和代理行為。
  • 更多方法 OPTIONSPUTDELETETRACECONNECTPATCH 為後續 RFC 引入,並非 1.1 核心)。

(下面的區別瞭解即可)

  • 虛擬主機支持:Host 頭在 1.1 中是必需字段,讓同一 IP 上託管多個域名成為主流。
  • 傳輸能力增強:支持分塊傳輸(Transfer-Encoding: chunked),可在未知內容長度時流式發送;支持 100-continue 協商。
  • 斷點續傳與部分內容:Range/Accept-Ranges206 Partial Content,有效減少重傳。

HTTP 1.1 缺點

  • 隊頭阻塞:1個TCP就是1個管道,同一個TCP內必須等前面一個請求完成,才能發送後一個請求(請求響應等待模型)。
  • HTTP頭大且重複發送:頭部沒有壓縮,頭部冗餘信息(重複的頭部信息)
  • 瀏覽器對TCP連接數量限制:Chrome 最多允許對同一個 Host(域名) 建立六個 TCP 連接,不同的瀏覽器有一些區別,這就意味着併發量受限。
假設我們還處在 HTTP/1.1 時代,那個時候沒有多路傳輸,當瀏覽器拿到一個有幾十張圖片的網頁該怎麼辦呢?肯定不能只開一個 TCP 連接順序下載,那樣用户肯定等的很難受,但是如果每個圖片都開一個 TCP 連接發 HTTP 請求,那電腦或者服務器都可能受不了,要是有 1000 張圖片的話總不能開 1000 個TCP 連接吧,你的電腦同意 NAT 也不一定會同意。
所以,瀏覽器對同一個host的TCP連接數量有限制!

HTTP 2 與1.1的區別

  1. 頭部壓縮
  2. 多路複用
  3. 服務端推送
頭部壓縮

Http1.x僅針對HTTP的body部分進行壓縮,通過Content-Encoding來規定如何編碼壓縮。但並未對請求頭進行壓縮。而Http2開始對頭部字段(包括狀態行)進行壓縮——使用了HPACK算法。

用通俗的語言解釋下就是,頭部壓縮需要在支持 HTTP/2 的瀏覽器和服務端之間:
  • 維護一份相同的靜態字典(Static Table),包含常見的頭部名稱,以及特別常見的頭部名稱與值的組合;
  • 維護一份相同的動態字典(Dynamic Table),可以動態地添加內容;
  • 支持基於靜態哈夫曼碼錶的哈夫曼編碼(Huffman Coding);

P.S. Huffman編碼是基於Huffman 樹——一種 「最優二叉樹」(其帶權路徑長度WPL最小)構建的編碼,作用是將字符/數字用一種變長編碼表示,頻率高的字符串所用的編碼越短,反之所用編碼越長,從而起到壓縮最終的字符串的效果。

靜態字典 保存了HTTP中常見的請求頭name-value(有的只有name), HTTP/2 中的靜態字典如下(以下只截取了部分,完整表格在這裏):

Index Header Name Header Value
1 :authority
2 :method GET
3 :method POST
4 :path /
5 :path /index.html
6 :scheme http
7 :scheme https
8 :status 200
... ... ...
32 cookie
... ... ...
60 via
61 www-authenticate

注意​​:新條目總是插入到索引62的位置,舊條目會被向下推。所以動態表的索引是​​從62開始向上增長​​的

動態字典 是在一個HTTP2連接(1個TCP連接)生命週期內的,當一個HTTP2連接結束就會重置動態字典(情況),動態字典初始情況是空的。

索引 (Index) 頭部名 (Header Name) 頭部值 (Header Value) 説明
1-61 ... ... ​​靜態表(固定不變)​​
62 (空) (空) ​​動態表起始邊界(空)

具體的交互過程

(圖片來自 Google 的性能專家 Ilya Grigorik 在 Velocity 2015 • SC 會議中分享的「HTTP/2 is here, let's optimize!」)

image.png

1.客户端第一次請求的頭
1.1. header name-value出現在(靜態)字典中的,可以使用(靜態)字典中的Index進行編碼。
1.2. 對於只有header name出現在靜態字典則更新客户端的動態字典 ,但此時編碼的name使用Index,而value繼續使用Huffman編碼的字符串(如上圖的19 Huffman("/resource"))。
1.3. 如果連header name都沒有出現在靜態字典,則對name和value都採用Huffman編碼。(如上圖的 Huffman("custom-hdr"))

2.當服務端收到這個請求頭,對於靜態字典/動態字典中沒有的頭部字段分兩種情況處理
2.1.對於只有header name出現在字典中的,更新動態字典
2.2對於header name-value都沒出現在字典中的,更新動態字典 (此時服務端已經同步了客户端的動態字典,服務端響應時,動態字典內name-value會用Index進行編碼)

3.當客户端收到響應頭,也會和第2步一樣處理,更新動態字典(至此,客户端和服務端的動態字典同步了

注意:

  1. 客户端/服務端動態字典的同步是在不斷請求-響應中逐步同步的,而不是一次性同步的。==HTTP/2頭部壓縮高效的關鍵——通過在多次請求間複用和累積動態字典,可以大幅減少後續請求的頭部大小。==
  2. 動態字典的生命週期和一個 HTTP/2 連接(通常對應一個 TCP 連接)一致。 當 TCP/HTTP2 連接斷開時,動態字典也隨之失效(清空)。(所以這也是為什麼上述第3步客户端收到響應頭還要繼續更新動態字典)

最終效果:能把Http1.x頭部傳輸的長度壓縮大概一半以上,效果非常好。

多路複用

三個概念

Stream

  • HTTP2可以在1個TCP的基礎上劃分多個stream(應用層的一個虛擬通道)。
  • 每個stream都有一個編號streamID,客户端建立的streamID是奇數,服務端建立的streamID是偶數(發請求的時候建立,服務端建立streamID是為了實現server push)。
    Message:對應1個請求或1個響應。
    Frame:HTTP2中最小通信數據單元,1個Message被拆分成多個Frame傳輸。

多路複用原理

  • 1個Stream只用來傳輸1次請求+1次響應,使用一個Stream(標記StreamID)發送完請求Frame完後,會使用同樣的StreamID的Stream將響應Frame返回。streamID不能複用(請求響應結束後,下一個請求需要使用新的Stream)。可配置Stream編號的上限,比如128個,當編號用完,則關閉TCP連接。
  • 不同的請求用了不同的Stream, Stream之間Message的傳輸是併發的,所以http請求時併發的(就是説stream之間的幀是可以亂序發的,而同一個stream裏面的幀是串行的)。
  • 匹配幀:幀頭有streamID, 從而判斷Frame屬於哪一個stream

下圖形象描述了Stream、Message和Frame的關係以及併發的原理:
image.png

下圖描述了多路複用後的效果:
image.png

服務端推送

服務端推送主要使用於Get請求(用於優化靜態資源的訪問):
場景:訪問html時,先請求html再請求css,發生了兩次請求,有傳播延遲,實際可以合併為一次請求。
實現:當客户端訪問服務端的某個資源時,服務端除了響應第一個資源,還主動推送其他資源。

image.png

協商緩存和強緩存

1.Http緩存包括協商緩存和強緩存,通過請求頭和響應頭完成對緩存策略的執行。
2.強緩存: 將資源存在瀏覽器/客户端本地,下次訪問url時能直接使用本地的緩存資源。
3.協商緩存:將資源存在瀏覽器/客户端本地,下次訪問url需要發請求給服務端判斷本地的資源是否有效,如果服務端響應説有效,就繼續使用本地資源。

http1.0時代

1.基於Expires頭字段實現強緩存,基於Last-Modified/If-Modified-Since頭字段實現協商緩存。

2.Last-Modified是資源上次(在服務器上)被修改的時間,通過修改時間是否變化來判斷資源是否過期。

http1.1時代

1、1.0時代的ExpiresLast-Modified/If-Modified-Since這些頭字段可以繼續使用,但增加了新字段(Cache-ControlEtag/If-None-Match)來控制:

  • 基於Cache-Control頭字段的max-age屬性實現強緩存。(max-age的優先級高於Expires
  • 基於 Etag/If-None-Match頭實現協商緩存。 (If-None-Match優先級高於If-Modified-Since,即如果請求頭有If-None-Match並和服務器請求,則會忽略If-Modified-Since

2、Etag是資源內容的一個hash標誌,一般是基於資源位文件的名稱/大小等元數據計算出來的一個hash值。不依據文件內容計算hash值是因為這個計算量太大,並且可以服務端可以緩存這個hash值,並不是每次響應請求都要計算一次.

緩存流程

強緩存:

1.第一次請求url得到響應如下,下載並緩存該資源。可以看到Cache-Controlmax-age設置了一個時間(單位秒),即資源的保鮮期。

HTTP/1.1 200 OK
Content-Type: application/javascript
Content-Length: 1024
Cache-Control: max-age=604800
ETag: W/"5c2d1a37a1e6ceecbad3deeff0fd68a5"
last-modified: Thu, 10 Jul 2025 02:53:06 GMT

2.第二次訪問該url時:當超過max-age保鮮期,則會重新發送請求,否則從內存/磁盤中讀取響應內容。

協商緩存:
1.第一次請求url的響應中有Etag頭字段的。
2.當強緩存失效(超過max-age保鮮期),則訪問該資源時會將向該資源發起請求:把資源之前響應中的Etag值放入請求頭的If-None-Match字段

GET /resources/xx.js HTTP/1.1
Host: example.com
Accept: application/javascript
Connection: keep-alive
If-None-Match: "5c2d1a37a1e6ceecbad3deeff0fd68a5"

3.服務器會判斷If-None-Match和資源的hash值是否相同,相同則返回304,否則返回200。
4.請求如果返回304(not modified,響應頭如下)説明資源未修改,可以繼續使用本地緩存,並且刷新保鮮期(根據響應的max-age重新計算)和ETag;否則在響應的body中加入資源的內容返回給客户端。

HTTP/1.1 304 OK
Content-Type: application/javascript
Content-Length: 1024
Cache-Control: max-age=604800
ETag: W/"5c2d1a37a1e6ceecbad3deeff0fd68a5"
last-modified: Thu, 10 Jul 2025 02:53:06 GMT

P.S 如果是基於Last-Modified的協商緩存,原理也差不多,只是把字段換一下,請求會攜帶上次響應的Last-Modified放在請求頭字段If-Modified-Since中,然後判斷響應是否為304。

相關問題

1、Cache-Control頭字段有哪些屬性?

  1. public: 允許響應被任何緩存存儲,包括瀏覽器和中間代理服務器。
  2. private: 只允許響應被單個用户的瀏覽器緩存,不能被中間代理服務器(比如cdn服務器)存儲。
  3. no-cache: 禁止強緩存。這並不意味着不緩存,而是意味着在使用緩存之前必須進行驗證(要協商緩存)。
  4. no-store: 禁止任何緩存存儲請求或響應數據。
  5. max-age=seconds: 指定從請求時間開始,緩存可被視為新鮮的秒數。

2、Etag解決了Last-Modified的哪些問題?

Last-Modified/If-Modified-Since的缺陷:

  • 只能精確到秒,如果文件是毫秒級別的更新則Last-Modified不變;
  • 文件內容不一定變化,但修改時間變了;
  • 負載均衡、分佈式的情況下,資源的Last-Modified不一致。

建議應該保留Last-Modified字段: 有些代理服務器或CDN會去掉/修改Etag或者不支持If-None-Match,所以需要Last-Modified來兜底。或一些要考慮速度的場景Last-Modified的生成比Etag快。

HTTP狀態碼

RFC 規定 HTTP 的狀態碼為三位數,被分為五類:

  • 1xx: 表示目前是協議處理的中間狀態,還需要後續操作。 比如 100 continue;101切換協議(http切換到websocket);
  • 2xx: 表示成功狀態。
  • 3xx: 重定向狀態,資源位置發生變動,需要重新請求。 比如 301表示永久重定向;302表示臨時重定向;304 Not Modified​ 表示資源未修改;
  • 4xx: 請求報文有誤。400表示錯誤請求;401表示認證; 比如 403表示未授權;404 not Found;405表示方法不允許;
  • 5xx: 服務器端發生錯誤。

GET和POST的區別

對比項 GET POST
參數位置 通過 URL 傳參(?key=value 放在請求體(Request Body)中
是否有請求體 無請求體(理論上是可以有,但實現上不帶請求體) 有請求體
安全性 參數暴露在 URL 中,不安全 參數在請求體中,相對安全(但仍需 HTTPS)
冪等性 冪等(同樣請求多次結果相同) 非冪等(多次提交可能造成重複數據)
緩存 默認可被緩存(瀏覽器會緩存 GET 請求) 默認不會被緩存
長度限制 URL 有長度限制(約 2KB~8KB,依瀏覽器/服務器不同) 理論上無限制
編碼 會被URL 編碼,中文空格'/'等非ASCII會被轉義。 支持多種編碼,比如: multipart/form-data:二進制application/x-www-form-urlencoded:鍵值對文本

【參考】

(建議精讀)HTTP靈魂之問,鞏固你的 HTTP 知識體系
HTTP/2 頭部壓縮技術介紹
HTTP/2 牛逼在哪?

四、HTTPs

基礎概念

TLS(Transport Layer Security)即傳輸層安全協議,以前也叫SSL。它是介於應用層和傳輸層之間的一層協議,起到加密作用。

對稱秘鑰加密: 雙方用同一個秘鑰來加密和解密數據。比如AES(Advance Encrypt Standard), SHA-256。

非對稱加密算法(公鑰私鑰):私鑰保存在服務器,公鑰明文的形式發給客户端。私鑰加密的只有公鑰能解,公鑰加密的只有私鑰能解。比如 RSA加密算法 。

對比:對稱加密更快,非對稱加密更安全(對稱加密,秘鑰泄漏的風險較大)。

數字證書 包含了如下信息:

  • 持有者信息 (域名、國家)
  • CA信息 (頒發機構、非對稱加密算法)
  • 證書有效期
  • 公鑰 (自己服務器生成的公鑰私鑰中的公鑰)
  • 數字簽名 (這個是對上述內容加密了的密文,叫簽名)
證書是明文傳輸的,通過簽名可以校驗證書是否被篡改。

CA: 數字證書權威機構,作用:頒發數字證書。(數字證書籤名使用了CA的私鑰,瀏覽器保存了CA的公鑰,因此瀏覽器可以驗證數字簽名的正確性)

TLS 四次握手

(圖片來自「技術蛋老師」的這期視頻)

image.png

流程:
1、Client -> Server :

  • 告知客户端的TLS版本,加密套件,第1隨機數。

2、Server -> Client

  • Server Hello: 確定TSL版本,選擇的加密算法,第2隨機數。
  • 發送數字證書(包含服務端生成的公鑰)

3、Client -> Server

  • 客户端通過內置的CA機構的公鑰驗證數字證書是否有效,則認為公鑰是安全的。使用公鑰加密預主密鑰(即第3個隨機數),發送給服務端。
  • 此時客户端和服務端都是通過同樣的方式來生成會話密鑰:第1隨機數+第2隨機數+預主密鑰。

4、Server -> Client

  • 服務端使用私鑰(之前生成的公鑰私鑰對)解密,拿到預主密鑰,生成會話密鑰
  • 回覆確認。

之後,採用會話密鑰進行加密通信。

為什麼要CA 簽名?
保證證書不被篡改/偽造,即公鑰是安全可用的。
數字證書包含了公鑰,並且被CA機構的私鑰做了簽名,而瀏覽器內置了各CA機構的公鑰,可以用來驗籤(也就是能解密)這個簽名。如果驗籤成功,説明這個證書是可信任的,確實是CA簽發的,不是被第三方偽造的。

非對稱加密 在該過程的作用?
用來安全的傳輸「預主密鑰」,換句話説就是用來保證安全下發「會話密鑰」。

為什麼會話過程中 使用對稱加密?
對稱加密速度快,用於會話通信的加密效率高。

隨機數的作用?
使用隨機數來生成秘鑰,保證了會話密鑰的唯一性和不確定性,更安全。

HTTPS和HTTP的區別

對比維度 HTTP HTTPS
​協議安全性​​ 明文傳輸,數據容易被竊聽、篡改或中間人攻擊 通過SSL/TLS協議對數據進行加密,確保數據的保密性和完整性
​默認端口​​ 80 443
​​證書要求​​ 不需要證書 需要向可信的證書頒發機構(CA)申請SSL/TLS證書,以驗證服務器身份
​​性能與速度​​ 無加密開銷,連接建立快 加密/解密會消耗額外計算資源,連接建立需更多步驟,但現代技術(如TLS 1.3)已大幅縮小差距

相關問題

1、HTTPS一定絕對安全嗎?

不一定。雖然 HTTPS 提供了加密和身份驗證來保護傳輸中的數據安全,但它並非絕對安全,存在被抓包和中間人攻擊的風險:

  1. 比如你主動信任某個抓包工具,就構造出一個“中間人”進行解密傳輸,抓包工具可以惡意獲取你的敏感信息。
  2. 如果你電腦中病毒,被植入惡意的中間人根證書,此時是你客户端不安全導致的中間人攻擊;
  3. 比如惡意wifi這種作為“中間人服務器”可以給你下發假「公鑰證書」,導致後續通信可以被解密、泄漏。但是要發生這種場景是有前提的,前提是用户點擊接受了中間人服務器的證書。

2、HTTPS如何抓包?

  1. 可以使用wireshakewhistle這類代理工具進行抓包(讓本地HTTP請求發到本地的代理服務器,然後由代理服務器發出去)。
  2. 對於HTTPS而言就需要安裝代理工具提供的根證書(放到電腦/手機的指定位置,讓操作系統內能訪問)
  3. 比如:whistle——抓包https請求的解決辦法

【參考】

視頻:HTTPS是什麼?加密原理和證書。SSL/TLS握手過程
HTTPS 一定是安全的嗎?

五、DNS

DNS是什麼?

DNS( Domain Name System)即域名系統,是一種應用層的協議和分佈式數據庫服務的結合。DNS協議告訴網絡如何通過域名查詢出IP,而DNS服務器則是一個分佈式數據庫,存放了域名到IP的映射。

域名
www.example.com實際可以看着www.example.com.(後面還有個.

  • .就是根域名。
  • .com就是頂級域名(TLD)。還有諸如.net,.org等等。
  • example.com就是一級域名。基於這個一級域名可以劃分二級、三級域名。比如www就是www.example.com的二級域名、map就是map.baidu.com的二級域名。

IP
連入網絡的計算機都具有一個IP地址,作為通信的地址,它由4個數組成(總共32位),每個數是0~255。比如下面的IP地址:

220.181.7.203

由於IP不便於記憶,所以出現了域名來指代IP,當然實際DNS中域名:IP並不一定是1:1.

對應關係 描述 主要目的
​一個域名 → 多個IP​ 在DNS中為同一域名配置多條A記錄(IPv4),每條記錄指向一個不同的服務器IP地址。 實現負載均衡與高可用性
​多個域名 → 一個IP​ 在DNS中將不同的域名解析到同一個IP地址,服務器根據HTTP請求頭中的域名信息來提供不同的網站內容。 節約IP資源,降低託管成本(虛擬主機)
​一個域名 → 一個IP​ 傳統的簡單映射關係,通常用於對獨立性或安全性有特殊要求的服務。 簡化管理,滿足特定需求

DNS服務器的類型和層次結構

DNS服務器是具有層次結構的,分為根域名服務、頂級域名服務器、權威域名服務器,分層結構如下圖:
image.png

DNS 解析的過程

DNS兩種查詢方案
  1. 遞歸式查詢
  2. 迭代式查詢

遞歸式查詢
image.png

  1. ​用户請求​​:用户在自己的電腦上輸入網址 www.baidu.com,電腦首先向​​本地域名服務器​​(通常由ISP提供)發起一個​​遞歸查詢​​請求。
  2. ​查詢根服務器​​:本地域名服務器收到請求後,向DNS體系中的​​根域名服務器​​發起查詢。
  3. ​返回頂級域地址​​:根域名服務器不直接提供具體IP,但它知道頂級域(如.com)的信息。它告訴本地域名服務器:”我不知道 www.baidu.com的IP,但我告訴你負責 .com域的​​頂級域名服務器​​的地址,你去問它。“
  4. ​查詢頂級域服務器​​:本地域名服務器根據收到的地址,向 ​​.com 頂級域名服務器​​發起查詢。
  5. ​返回權威服務器地址​​:.com 頂級域名服務器回覆本地域名服務器:”我不知道 www.baidu.com的具體IP,但我告訴你負責 baidu.com這個域的​​權威域名服務器​​的地址,你去問它。“
  6. ​查詢權威服務器​​:本地域名服務器再向管理 baidu.com域的​​權威域名服務器​​發起查詢。
  7. ​返回最終IP地址​​:權威域名服務器查詢自己的記錄,確認了 www.baidu.com對應的IP地址,並將這個​​最終的IP地址​​返回給本地域名服務器。
  8. ​響應用户​​:本地域名服務器終於拿到了目標IP地址,它將這個IP地址​​返回給用户的電腦​​。至此,電腦獲得了IP,就可以開始與百度服務器建立連接了。

​簡單總結​:整個過程就像問路,本地DNS服務器替你一層一層地去問(根服務器->頂級域服務器->權威服務器),直到拿到最終地址(IP)後回來告訴你。

迭代式查詢
image.png

  1. 用户在電腦瀏覽器中輸入 www.baidu.com後,用户的電腦(DNS客户端)向​​本地域名服務器​​(通常由網絡服務商ISP提供)發起一個​​遞歸查詢​​請求。
  2. 本地域名服務器收到請求後,首先向根(.)域名服務器發起第一次迭代查詢。
  3. 根域名服務器返回一個頂級域名服務器的地址,讓本地域名服務器繼續查這個地址。
  4. 本地域名服務器向頂級(.com)域名服務器第二次​​迭代查詢。
  5. 頂級域名服務器返回一個權威域名服務器的地址,讓本地域名服務器繼續查這個地址。
  6. 本地域名服務器向​權威(baidu.com)域名服務器​發起第三次​​迭代查詢​。
  7. 權威域名服務器查詢自己的記錄,確認了主機 www.baidu.com對應的確切IP地址,並將這個​​最終的IP地址​​返回給本地域名服務器。
  8. 本地域名服務器終於拿到了最終的IP地址,它將這個IP地址​返回給用户的電腦​​。

“遞歸查詢”意味着用户要求本地域名服務器​​必須​​給出最終的IP地址,而不能只是返回一個線索。

「迭代式查詢」方案中,其實是既有遞歸查詢,又有迭代查詢的,(見「迭代式查詢」圖)主機查詢本地域名服務器是採用遞歸查詢,本地域名服務器去查其他服務器是迭代查詢的。

根服務器和頂級域名服務器的作用是“導航”和“指路”,而最終的目的地(IP地址)只有域名的擁有者所管理的“權威域名服務器”才知道。​根域名服務器和頂級域名服務器是不會存目標域名的IP,因為像.com這種有上億個網站,都存入頂級域名服務器存儲和查詢壓力都是非常大的。所以採用這種委託機制。

由於「遞歸式查詢」對根域名服務器的壓力大,所以實際互聯網採用了「迭代式查詢」方案(遞歸查詢和迭代查詢混用的形式)。DNS的傳輸層協議默認情況下使用的UDP協議(端口是53),一些情況下也會用TCP協議輔助。

DNS緩存

一方面是本地域名服務器上有緩存(一般有一個較短的存活時間,DNS記錄的TTL表示),另一方面是用户的主機(電腦手機設備)有緩存。

域名解析緩存路徑:瀏覽器緩存(幾十秒)——>系統hosts文件——>本地域名服務器緩存(幾天)——>向外部查詢

【參考】

字節面試被虐後,是時候搞懂 DNS 了
8 張圖帶你徹底搞懂 DNS 域名解析過程
36 張圖詳解 DNS :網絡世界的導航

六、CDN

CDN作用和原理

CDN (全稱 Content Delivery Network),即內容分發網絡。基於現有網絡,構建的一個虛擬網絡,這個虛擬網絡由分佈在各地的邊緣服務器組成。 通過cdn可以將靜態

簡單總結CDN原理:本地域名服務器向DNS發送查詢請求,對於使用了CDN的域名,那麼DNS會返回一個CNAME記錄(而不是一個ip地址,CNAME是指向CDN網絡的域名)。本地域名服務器,會請求查詢這個 CNAME 對應的IP。從而進入CDN的智能調度網絡中,綜合下面因素,找到合適的CDN節點:
1)IP 物理地址,查表找最近的
2)找同一個運營商網絡中的
3)考慮節點的服務能力,響應速度

DNS與CNAME記錄的作用

  1. CDN智能調度的起點是 ​​CNAME記錄​​。當你的網站接入了CDN服務後,在域名的DNS設置中,不再是直接將域名指向源站服務器的IP地址(A記錄),而是指向一個由CDN服務商提供的域名,也就是CNAME記錄(例如 www.example.com指向 www.example.com.c.cdnhwc1.com)。這個過程可以理解為一種“域名解析權的移交”。
  2. 當本地DNS服務器查詢到CNAME記錄後,它需要對這個新的域名(即CDN服務商提供的域名)進行新一輪的DNS解析,在這輪解析中當到達了權威域名服務器時,會進入CDN的​​全局負載均衡系統(GSLB)​​。
  3. GSLB會根據多種複雜的實時因素來決定將哪個邊緣節點的IP地址返回給用户,而不僅僅是簡單的地理位置最近。

CDN訪問過程

(圖來自 為了搞清楚CDN的原理,我頭都禿了.. )

  1. 先經過 本地DNS服務器(LDNS) 解析,如果 LDNS 命中緩存,直接返回給用户。
  2. LDNS 未命中緩存,繼續DNS查詢。
  3. DNS查詢(經過根/頂級/權威域名服務器查詢),返回了一個CNAME
  4. LDNS發現是一個CNAME(還是 一個域名),繼續請求DNS解析。此時CNAME解析會返回一個最優的邊緣節點IP
  5. LDNS響應IP給用户
  6. 用户使用這個IP請求邊緣節點的資源
  7. 返回資源

CDN緩存

我們知道當瀏覽器的強緩存失效,就會走協商緩存,但如果我的網站使用了cdn呢,協商緩存的驗證請求發給誰呢?

cdn緩存本身是HTTP緩存的內容,遵循HTTP協議。還記得Cache-Control的public屬性嗎,就是用來允許響應可被CDN、代理服務器緩存的。

當用户請求到cdn的資源時,cdn的資源也會有一個緩存期限(依據Cache-Control:max-age=<seconds>設置的保鮮期限),如果在保鮮期限內則直接返回,否則會重新向服務器請求(協商緩存)。

CDN緩存是中間層緩存,與瀏覽器緩存是不同的緩存層次。

【參考】

為了搞清楚CDN的原理,我頭都禿了..

tmp.png

八、websocket和SSE

websocket

什麼是websocket
HTTP這種單向請求的特點,註定瞭如果服務器有連續的狀態變化,客户端要獲知就非常麻煩。我們只能使用"輪詢":每隔一段時候,就發出一個詢問,瞭解服務器有沒有新的信息。最典型的場景就是聊天室。

(圖片來自WebSocket 教程 )
image.png

一句話解釋:Client給Server發信息的同時,Server可以給Client發送信息,是一種平等的對話,是一種全雙工通信協議。

工作原理

  1. 握手階段: (1次http握手)

    • 客户端使用 HTTP(S) 發起一個特殊的請求(包含 Upgrade: websocket 頭)。
    • 服務器返回 101 Switching Protocols 響應,確認升級協議。
  2. 建立連接後

    • 雙方可在同一個 TCP 通道上互相推送數據,不再需要 HTTP 請求頭。
    • 數據以輕量的二進制幀(Frame)格式傳輸,延遲低

特點如下:

特點 説明
全雙工通信 客户端和服務端都可以主動發送消息
基於 TCP 保證可靠傳輸,建立後保持 TCP 通道,減少連接開銷
輕量級幀結構 比 HTTP 請求頭小得多,傳輸效率高
跨域支持好 通過同源策略限制較少
客户端API

創建WebSocket實例時傳入一個url實現連接,url的協議必須是ws://或者wss://

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
};

ws上的方法有

方法 作用
new WebSocket(url) 創建連接(ws://wss://
ws.send(data) 發送字符串或二進制數據
ws.close() 主動關閉連接
ws.on() 監聽事件:open,message,error,close
P.S. ws.on('message', fn)和 ws.onmessage = fn 是一樣的,都可以使用。

ws上的屬性readyState有四種值:

  • CONNECTING:值為0,表示正在連接。
  • OPEN:值為1,表示連接成功,可以通信了。
  • CLOSING:值為2,表示連接正在關閉。
  • CLOSED:值為3,表示連接已經關閉,或者打開連接失敗。

P.S.由於websocket是一個client/server對等的協議,server端的API起始和客户端是一致的,就不介紹了。

心條機制-斷線重連

1.onerror和onclose的區別(弱網環境中的連接斷開)
在弱網環境中,如果網絡連接不穩定或中斷,WebSocket連接可能會被關閉。這種情況下,瀏覽器通常會觸發close事件而不是error事件。close事件會包含一些關於關閉原因的信息,開發者可以根據這些信息判斷連接是如何關閉的。

  • onerror的觸發條件error事件一般用於捕捉低級別的網絡錯誤或協議錯誤,例如無法解析服務器地址、send數據時服務器不可達、協議握手失敗等。這些錯誤通常在連接建立階段或數據傳輸過程中發生。
  • onclose的觸發條件close事件會在WebSocket連接關閉時觸發,無論關閉的原因是什麼,包括正常的關閉和由不正常導致的關閉(對於不正常關閉有一定延遲觸發,是底層TCP的keep-alive機制)。藉助close事件,開發者可以獲取更詳細的關閉信息,例如關閉狀態碼和關閉原因。

2.心跳機制和重連

  • 弱網環境中TCP容易斷開,然後觸發close事件,通常我們在onclose中發起重連,但可能會有一定延遲。
  • 為了避免這個延遲(發真正數據時才發現斷開了),所以協議裏規定了ping-pong機制(即心跳機制)——服務端發一個ping幀,客户端就會響應一個pong幀,反之亦然。
  • 針對ping幀,如果一方沒有按時收到pong幀,説明斷開了就會自動觸發close事件,然後重連。

SSE

什麼是SSE?

(圖片來自Server-Sent Events 教程)
image.png

一句話解釋:Client(Browse)請求Server後,Server可以持續向Client推送消息,Client通過事件監聽不斷接受消息直到結束連接。

原理:Server告訴Client發送的是一種事件流(即text/event-stream,協議層的約定),收到響應後並不立即結束,後面還能繼續收到消息,這些消息都是對這次請求的響應。實現關鍵是通過Server設置響應頭Content-Type:text/event-stream,而Client在處理響應時會一直監聽消息(onmessage)。

客户端API
EventSource方法和事件:
  1. EventSource的事件:

    • open事件:連接建立時觸發。
    • message事件:接收到來自服務器端的消息時觸發。
    • error事件:連接出錯時觸發。
  2. EventSource實例方法:

    • close():手動關閉SSE連接(關閉SSE連接就是關閉TCP連接的)。
  3. EventSource實例readyState屬性:

    • CONNECTING表示連接正在建立。
    • OPEN表示連接已經建立。
    • CLOSED表示連接已經關閉。

建立連接:

const sse = new EventSource("/api/v1/sse"); //創建實例就會自動連接
//sse.readyState可以判斷連接狀態

監聽信息:你可以使用EventSource的addEventListener方法監聽事件(包括自定義事件)

source.addEventListener('open', ()=>{})
source.addEventListener('message', (data)=>{})
source.addEventListener('error', ()=>{})
source.addEventListener('close', ()=>{})

message事件會被觸發多次,即可以分多次接受數據,你可以不斷拼接數據塊data實現文本展示「打字」效果。

服務端

1.設置響應頭

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

因為SSE推送的是實時信息,所以不需要緩存,設置no-cache;SSE連接是一個長連接,需要持續接受消息,不能接受一次消息就斷開TCP連接,設置keep-alive

2.發送數據
下面是雙方約定的響應數據的格式:

event: [messageEvent]
id: [num]
data: [textContent]
retry: [num]
: [comment]
  • event字段,表示(下面data數據對應的)自定義的事件類型,默認是message事件。瀏覽器可以addEventListener()監聽該事件。
  • id字段,表示data的編號,瀏覽器用lastEventId屬性讀取這個值,對於SSE斷開重連同步數據有用。
  • data字段,就是實際傳輸的文本。
  • retry字段,表示連接出錯後重試的次數,重試retry次後就關閉連接。一般只發一次,並且第一個發。
  • 你沒看錯,還有最後一種,:表示註釋,這個沒有業務作用,主要是防止時間過長TCP斷開連接。

一般可以省略 eventidretry,數據會長下面這樣,\n\n表示一個消息的結束標記(僅一個\n表示換行)。

data: some text\n\n

data: another message\n
data: with two lines\n\n

onmessage可以接受到兩個信息:
some textanother message\nwith two lines

4.如何結束?

方式一(推薦)自定義結束event:
自定義end事件,比如event: end 表示結束事件,Client監聽到end信息後主動close,關閉連接。

P.S. 在Nodejs中,調用res.end("event: end"); 瀏覽器收到事件後eventSource.close()(底層就是發送一個FIN報文,結束TCP連接)

方式二 server主動關閉:
通過響應頭的方式, 響應頭Connection: close表示本次 HTTP 響應結束後不再保持連接,服務器會在發送完數據後主動斷開。

response.setHeader('Connection', 'close');
P.S. 此時會觸發error事件,同時event.readyState=EventSource.CLOSED。判斷錯誤對象可以判斷是不是正常關閉,還是網絡錯誤導致的關閉。
和websocket的區別

1.協議

  • SSE是基於HTTP協議的(本身就是HTTP協議)
  • websocket是一種新協議(相對HTTP協議更加複雜),但需從HTTP升級(Upgrade)
    2.雙工性
  • SSE半雙工通信,只是服務端推送數據(不能邊發邊收)。
  • websocket是全雙工通信,可以一邊發送數據一邊接受數據。
    3.自動斷線重連
  • SSE 具有自動重連機制:當連接中斷時,瀏覽器會嘗試重新建立連接,確保持續地接收服務器的事件流。
  • websocket需要手動實現ping-pong機制來重連。

【參考】

WebSocket 教程
Server-Sent Events 教程

九、URL輸入到顯示發生了什麼

1. 緩存機制

  • 緩存檢查:根據URL判斷是否有內存或磁盤緩存。

    • 強緩存:如果緩存有效,直接使用本地資源,無需請求服務器。

2. DNS解析

  • 解析過程:將域名解析為IP地址。

    • 緩存層級

      1. 瀏覽器緩存
      2. 本地hosts文件
      3. 本地域名服務器
    • 查詢方式

      • 遞歸查詢
      • 迭代查詢
    • 域名服務器類型

      • 根域名服務器(.
      • 頂級域名服務器(如.com
      • 權威域名服務器(如baidu.com
      • 本地域名服務器
    • CDN:可能在此階段介入,加速資源訪問。

3. TCP三次握手

  • 建立連接

    1. 客户端發送SYN報文。
    2. 服務器響應SYN-ACK報文。
    3. 客户端發送ACK報文,完成連接建立。

4. HTTPS連接(TLS握手)

  • 四次握手

    • 協商加密算法和密鑰。
    • 驗證服務器身份。
    • 建立安全通道。

5. HTTP請求與響應

  • 請求構建:瀏覽器構建HTTP請求,通過TCP連接發送。
  • 協議版本

    • HTTP/1.1:複用TCP連接,但存在隊頭阻塞問題。
    • HTTP/2:多路複用,避免隊頭阻塞。
  • 響應處理

    • 狀態碼

      • 304:資源未修改,使用緩存。
      • 301:永久重定向。
      • 302:臨時重定向。
      • 200:成功,繼續解析和渲染。

6. 瀏覽器渲染

  1. 解析

    • HTML解析為DOM樹。
    • CSS解析為CSSOM樹。
  2. 樣式計算:結合DOM和CSSOM生成渲染樹(Render Tree)。
  3. 佈局:計算元素位置和尺寸(Layout Tree)。
  4. 繪製:生成繪製指令。
  5. 柵格化與合成:將繪製結果轉換為像素並顯示。

7. 迴流與重繪

  • 迴流(Reflow):佈局變化(如尺寸、位置改變)。
  • 重繪(Repaint):樣式變化(如顏色、可見性改變)。
user avatar alibabawenyujishu Avatar pengxiaohei Avatar kobe_fans_zxc Avatar vleedesigntheory Avatar longlong688 Avatar inslog Avatar xinggandemuer_b5u1v2 Avatar dunizb Avatar woniuseo Avatar guixiangyyds Avatar didiaodekaishuiping Avatar kongsq Avatar
Favorites 75 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.