引言:
本文用通俗簡練的語言又不失細節(自認為doge)地介紹TCP/UDP、HTTP、HTTPs、DNS、CDN、Websocket和SSE等。本文面向求職面試人羣,比較全面的歸納了面試中計算機網絡涵蓋的面試點,你可以結合本文自行拓展深度和廣度。如果你準備時間不夠,更加推薦你看這篇文章!
如果錯誤或侵權之處歡迎指正和聯繫我。
一、OSI 七層模型
計算機網絡的7層模型,也稱為OSI七層參考模型,是一個概念框架,它將網絡通信功能劃分為七個抽象層,從下到上分別是:物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。
在實際應用中,通常抽象為5層模型,如下圖所示:
二、TCP和UDP
TCP和UDP的區別
1.總體
- TCP是基於連接的可靠的傳輸協議
- UDP是不基於連接的不可靠傳輸協議
2.報文頭
- TCP報文頭20個字節,除了端口、校驗和字段,還有seqNum, ackNum,標記位和窗口大小等。
- UDP報文頭8個字節,只有端口、包長度和校驗和字段。
(圖片來自TCP 頭格式有哪些?)
3.可靠性
- TCP 擁塞控制、流量控制、重傳機制、滑動窗口等機制保證傳輸質量
- UDP沒有
4.雙工性
- TCP 是點對點通信 全雙工通信
- UDP 支持一對一、一對多、多對一和多對多的交互通信。
TCP 三次握手
流程
(圖片來自三次握手過程是怎麼樣的?)
一次握手
客户端會隨機初始化序號(client_isn),將此序號置於 TCP 首部的「序列號」字段中,同時把 SYN 標誌位置為 1 ,表示 SYN 報文。接着把第一個 SYN 報文發送給服務端,表示向服務端發起連接,該報文不包含應用層數據,之後客户端處於 SYN-SENT 狀態。
二次握手
服務端收到客户端的 SYN 報文後,首先服務端也隨機初始化自己的序號(server_isn),將此序號填入 TCP 首部的「序號」字段中,其次把 TCP 首部的「確認應答號」字段填入 client_isn + 1, 接着把 SYN 和 ACK 標誌位置為 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 四次揮手過程是怎樣的?)
一次揮手
客户端打算關閉連接,此時會發送一個 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 最大報文存活時間) 後關閉
也可以動手自己畫畫這個流程圖,像下面這樣
為什麼兩次不行?
如果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提供的一種檢測連接是否存活的可選機制,主要用於:
- 檢測對端是否仍然可達
- 防止中間設備因超時斷開連接
- 釋放已失效連接佔用的資源
Keepalive工作原理
- 空閒檢測:當連接空閒時間超過
tcp_keepalive_time(默認2小時)後,開始發送探測包 - 探測間隔:每隔
tcp_keepalive_intvl(默認75秒)發送一次探測包 - 重試次數:最多嘗試
tcp_keepalive_probes(默認9次)次 - 判定失效:如果所有探測都無響應,則認為連接已斷開
基於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」)
【參考】
TCP 三次握手與四次揮手面試題——小林coding
TCP 重傳、滑動窗口、流量控制、擁塞控制——小林coding
三、HTTP
HTTP 1.1與1.0的區別
區別:
- 長連接:HTTP 1.1 默認使用持久連接(
Connection: keep-alive),同一 TCP 連接可複用多個請求/響應;HTTP/1.0 默認每次請求都新建連接(最重要的!)。 - 更完善的緩存機制:
Cache-Control、ETag/If-None-Match等,更準確和精細化控制緩存與節省帶寬。 - 升級與代理:
Connection:Upgrade、Via等,明確協議升級和代理行為。 - 更多方法
OPTIONS、PUT、DELETE、TRACE、CONNECT(PATCH為後續 RFC 引入,並非 1.1 核心)。
(下面的區別瞭解即可)
- 虛擬主機支持:
Host頭在 1.1 中是必需字段,讓同一 IP 上託管多個域名成為主流。 - 傳輸能力增強:支持分塊傳輸(
Transfer-Encoding: chunked),可在未知內容長度時流式發送;支持100-continue協商。 - 斷點續傳與部分內容:
Range/Accept-Ranges及206 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的區別
- 頭部壓縮
- 多路複用
- 服務端推送
頭部壓縮
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!」)
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步一樣處理,更新動態字典(至此,客户端和服務端的動態字典同步了)
注意:
- 客户端/服務端動態字典的同步是在不斷請求-響應中逐步同步的,而不是一次性同步的。==HTTP/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的關係以及併發的原理:
下圖描述了多路複用後的效果:
服務端推送
服務端推送主要使用於Get請求(用於優化靜態資源的訪問):
場景:訪問html時,先請求html再請求css,發生了兩次請求,有傳播延遲,實際可以合併為一次請求。
實現:當客户端訪問服務端的某個資源時,服務端除了響應第一個資源,還主動推送其他資源。
協商緩存和強緩存
1.Http緩存包括協商緩存和強緩存,通過請求頭和響應頭完成對緩存策略的執行。
2.強緩存: 將資源存在瀏覽器/客户端本地,下次訪問url時能直接使用本地的緩存資源。
3.協商緩存:將資源存在瀏覽器/客户端本地,下次訪問url需要發請求給服務端判斷本地的資源是否有效,如果服務端響應説有效,就繼續使用本地資源。
http1.0時代
1.基於Expires頭字段實現強緩存,基於Last-Modified/If-Modified-Since頭字段實現協商緩存。
2.Last-Modified是資源上次(在服務器上)被修改的時間,通過修改時間是否變化來判斷資源是否過期。
http1.1時代
1、1.0時代的Expires,Last-Modified/If-Modified-Since這些頭字段可以繼續使用,但增加了新字段(Cache-Control, Etag/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-Control的max-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頭字段有哪些屬性?
- public: 允許響應被任何緩存存儲,包括瀏覽器和中間代理服務器。
- private: 只允許響應被單個用户的瀏覽器緩存,不能被中間代理服務器(比如cdn服務器)存儲。
- no-cache: 禁止強緩存。這並不意味着不緩存,而是意味着在使用緩存之前必須進行驗證(要協商緩存)。
- no-store: 禁止任何緩存存儲請求或響應數據。
- 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 四次握手
(圖片來自「技術蛋老師」的這期視頻)
流程:
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 提供了加密和身份驗證來保護傳輸中的數據安全,但它並非絕對安全,存在被抓包和中間人攻擊的風險:
- 比如你主動信任某個抓包工具,就構造出一個“中間人”進行解密傳輸,抓包工具可以惡意獲取你的敏感信息。
- 如果你電腦中病毒,被植入惡意的中間人根證書,此時是你客户端不安全導致的中間人攻擊;
- 比如
惡意wifi這種作為“中間人服務器”可以給你下發假「公鑰證書」,導致後續通信可以被解密、泄漏。但是要發生這種場景是有前提的,前提是用户點擊接受了中間人服務器的證書。
2、HTTPS如何抓包?
- 可以使用
wireshake和whistle這類代理工具進行抓包(讓本地HTTP請求發到本地的代理服務器,然後由代理服務器發出去)。 - 對於HTTPS而言就需要安裝代理工具提供的根證書(放到電腦/手機的指定位置,讓操作系統內能訪問)
- 比如: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服務器是具有層次結構的,分為根域名服務、頂級域名服務器、權威域名服務器,分層結構如下圖:
DNS 解析的過程
DNS兩種查詢方案
- 遞歸式查詢
- 迭代式查詢
遞歸式查詢
- 用户請求:用户在自己的電腦上輸入網址
www.baidu.com,電腦首先向本地域名服務器(通常由ISP提供)發起一個遞歸查詢請求。 - 查詢根服務器:本地域名服務器收到請求後,向DNS體系中的根域名服務器發起查詢。
- 返回頂級域地址:根域名服務器不直接提供具體IP,但它知道頂級域(如
.com)的信息。它告訴本地域名服務器:”我不知道www.baidu.com的IP,但我告訴你負責.com域的頂級域名服務器的地址,你去問它。“ - 查詢頂級域服務器:本地域名服務器根據收到的地址,向 .com 頂級域名服務器發起查詢。
- 返回權威服務器地址:.com 頂級域名服務器回覆本地域名服務器:”我不知道
www.baidu.com的具體IP,但我告訴你負責baidu.com這個域的權威域名服務器的地址,你去問它。“ - 查詢權威服務器:本地域名服務器再向管理
baidu.com域的權威域名服務器發起查詢。 - 返回最終IP地址:權威域名服務器查詢自己的記錄,確認了
www.baidu.com對應的IP地址,並將這個最終的IP地址返回給本地域名服務器。 - 響應用户:本地域名服務器終於拿到了目標IP地址,它將這個IP地址返回給用户的電腦。至此,電腦獲得了IP,就可以開始與百度服務器建立連接了。
簡單總結:整個過程就像問路,本地DNS服務器替你一層一層地去問(根服務器->頂級域服務器->權威服務器),直到拿到最終地址(IP)後回來告訴你。
迭代式查詢
- 用户在電腦瀏覽器中輸入
www.baidu.com後,用户的電腦(DNS客户端)向本地域名服務器(通常由網絡服務商ISP提供)發起一個遞歸查詢請求。 - 本地域名服務器收到請求後,首先向根(
.)域名服務器發起第一次迭代查詢。 - 根域名服務器返回一個頂級域名服務器的地址,讓本地域名服務器繼續查這個地址。
- 本地域名服務器向頂級(
.com)域名服務器第二次迭代查詢。 - 頂級域名服務器返回一個權威域名服務器的地址,讓本地域名服務器繼續查這個地址。
- 本地域名服務器向權威(
baidu.com)域名服務器發起第三次迭代查詢。 - 權威域名服務器查詢自己的記錄,確認了主機
www.baidu.com對應的確切IP地址,並將這個最終的IP地址返回給本地域名服務器。 - 本地域名服務器終於拿到了最終的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記錄的作用
- CDN智能調度的起點是 CNAME記錄。當你的網站接入了CDN服務後,在域名的DNS設置中,不再是直接將域名指向源站服務器的IP地址(A記錄),而是指向一個由CDN服務商提供的域名,也就是CNAME記錄(例如
www.example.com指向www.example.com.c.cdnhwc1.com)。這個過程可以理解為一種“域名解析權的移交”。 - 當本地DNS服務器查詢到CNAME記錄後,它需要對這個新的域名(即CDN服務商提供的域名)進行新一輪的DNS解析,在這輪解析中當到達了權威域名服務器時,會進入CDN的全局負載均衡系統(GSLB)。
- GSLB會根據多種複雜的實時因素來決定將哪個邊緣節點的IP地址返回給用户,而不僅僅是簡單的地理位置最近。
CDN訪問過程
(圖來自 為了搞清楚CDN的原理,我頭都禿了.. )
- 先經過 本地DNS服務器(LDNS) 解析,如果 LDNS 命中緩存,直接返回給用户。
- LDNS 未命中緩存,繼續DNS查詢。
- DNS查詢(經過根/頂級/權威域名服務器查詢),返回了一個CNAME
- LDNS發現是一個CNAME(還是 一個域名),繼續請求DNS解析。此時CNAME解析會返回一個最優的邊緣節點IP
- LDNS響應IP給用户
- 用户使用這個IP請求邊緣節點的資源
- 返回資源
CDN緩存
我們知道當瀏覽器的強緩存失效,就會走協商緩存,但如果我的網站使用了cdn呢,協商緩存的驗證請求發給誰呢?
cdn緩存本身是HTTP緩存的內容,遵循HTTP協議。還記得Cache-Control的public屬性嗎,就是用來允許響應可被CDN、代理服務器緩存的。
當用户請求到cdn的資源時,cdn的資源也會有一個緩存期限(依據Cache-Control:max-age=<seconds>設置的保鮮期限),如果在保鮮期限內則直接返回,否則會重新向服務器請求(協商緩存)。
CDN緩存是中間層緩存,與瀏覽器緩存是不同的緩存層次。
【參考】
為了搞清楚CDN的原理,我頭都禿了..
八、websocket和SSE
websocket
什麼是websocket
HTTP這種單向請求的特點,註定瞭如果服務器有連續的狀態變化,客户端要獲知就非常麻煩。我們只能使用"輪詢":每隔一段時候,就發出一個詢問,瞭解服務器有沒有新的信息。最典型的場景就是聊天室。
(圖片來自WebSocket 教程 )
一句話解釋:Client給Server發信息的同時,Server可以給Client發送信息,是一種平等的對話,是一種全雙工通信協議。
工作原理:
-
握手階段: (1次http握手)
- 客户端使用 HTTP(S) 發起一個特殊的請求(包含
Upgrade: websocket頭)。 - 服務器返回
101 Switching Protocols響應,確認升級協議。
- 客户端使用 HTTP(S) 發起一個特殊的請求(包含
-
建立連接後:
- 雙方可在同一個 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 教程)
一句話解釋:Client(Browse)請求Server後,Server可以持續向Client推送消息,Client通過事件監聽不斷接受消息直到結束連接。
原理:Server告訴Client發送的是一種事件流(即text/event-stream,協議層的約定),收到響應後並不立即結束,後面還能繼續收到消息,這些消息都是對這次請求的響應。實現關鍵是通過Server設置響應頭Content-Type:text/event-stream,而Client在處理響應時會一直監聽消息(onmessage)。
客户端API
EventSource方法和事件:
-
EventSource的事件:
- open事件:連接建立時觸發。
- message事件:接收到來自服務器端的消息時觸發。
- error事件:連接出錯時觸發。
-
EventSource實例方法:
close():手動關閉SSE連接(關閉SSE連接就是關閉TCP連接的)。
-
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斷開連接。
一般可以省略 event、id、 retry,數據會長下面這樣,\n\n表示一個消息的結束標記(僅一個\n表示換行)。
data: some text\n\n
data: another message\n
data: with two lines\n\n
onmessage可以接受到兩個信息:
some text 和 another 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地址。
-
緩存層級:
- 瀏覽器緩存
- 本地hosts文件
- 本地域名服務器
-
查詢方式:
- 遞歸查詢
- 迭代查詢
-
域名服務器類型:
- 根域名服務器(
.) - 頂級域名服務器(如
.com) - 權威域名服務器(如
baidu.com) - 本地域名服務器
- 根域名服務器(
- CDN:可能在此階段介入,加速資源訪問。
-
3. TCP三次握手
-
建立連接:
- 客户端發送
SYN報文。 - 服務器響應
SYN-ACK報文。 - 客户端發送
ACK報文,完成連接建立。
- 客户端發送
4. HTTPS連接(TLS握手)
-
四次握手:
- 協商加密算法和密鑰。
- 驗證服務器身份。
- 建立安全通道。
5. HTTP請求與響應
- 請求構建:瀏覽器構建HTTP請求,通過TCP連接發送。
-
協議版本:
- HTTP/1.1:複用TCP連接,但存在隊頭阻塞問題。
- HTTP/2:多路複用,避免隊頭阻塞。
-
響應處理:
-
狀態碼:
304:資源未修改,使用緩存。301:永久重定向。302:臨時重定向。200:成功,繼續解析和渲染。
-
6. 瀏覽器渲染
-
解析:
- HTML解析為DOM樹。
- CSS解析為CSSOM樹。
- 樣式計算:結合DOM和CSSOM生成渲染樹(Render Tree)。
- 佈局:計算元素位置和尺寸(Layout Tree)。
- 繪製:生成繪製指令。
- 柵格化與合成:將繪製結果轉換為像素並顯示。
7. 迴流與重繪
- 迴流(Reflow):佈局變化(如尺寸、位置改變)。
- 重繪(Repaint):樣式變化(如顏色、可見性改變)。