Catalog
- HTTP 協議
1.1 特性
1.2 HTTP 報文
1.3 會話
1.4 緩存
1.5 缺點
1.6 HTTP/1.1
1.7 HTTP/2.0
1.8 HTTP/3.0
1.9 HTTPS - DNS 協議
2.1 DNS 查找過程
2.2 遞歸查詢和迭代查詢
2.3 HTTPDNS - TCP 協議
3.1 OSI 網絡模型
3.2 TCP 數據包大小
3.3 TCP 報文首部字段注析
3.4 三次握手
3.5 四次揮手
3.6 重傳機制
3.7 滑動窗口
3.8 擁塞控制
3.9 快速打開(TFO)
3.10 TCP keep-alive 機制
3.11 安全
3.12 TCP 與 UDP 的區別 - IP 協議
4.1 特性
4.2 IP 報頭
4.3 IP 地址類型
4.4 IP 地址分類
書單
- 《網絡是怎樣連接的》
- 《圖解 HTTP》
- 《圖解 TCP/IP》
- 《透視 HTTP 協議》
- 《瀏覽器原理》
真的會了嗎?
- [緩存相關]
- 講一講強緩存和協商緩存?
- 有了【Last-Modified, If-Modified-Since】為何還要有【ETag、If-None-Match】
- 有關 HTTP 緩存的首部字段說一下
- [TCP/IP 相關]
- HTTP 和 TCP 的關系
- TCP 三次握手和四次揮手?以其存在意義。為什么揮手要多一次。(既保證了可靠傳輸(兩次不可靠,因此要三次),且效率最高(成本最低)。)
- 在一次傳輸中它是如何保證每個數據包之間的順序的?
- [HTTP 相關]
- HTTP 中的 keep-alive 有了解嗎?
- 說說 HTTP/1.1 長連接、管線化
- HTTP/2 特性
- HTTP/2 對比 HTTP/1.1
- HTTP/2 都有哪些特性?頭部壓縮的原理?
- HTTP/2 是怎么解決隊頭阻塞的
- HTTP/2 是如何壓縮頭部的
- 具體說一下 HTTP/2 中的多路復用
- HTTPS 握手
- HTTPS 請求的什么時候用的對稱加密什么時候非對稱加密
- HTTP3 特性
- GET 和 POST 的區別
- GET 就一定是冪等的嗎?
- 狀態碼。301/302/303/304/401/403/404/500/503?
- 狀態碼。204 和 304 分別有什么作用?
- HTTP 和 HTTPS 握手差異?
- 為什么說 HTTPS 比 HTTP 安全呢
- 簡單講了一下非對稱加密的握手過程
- 證書簽名過程和如何防止被串改
- TCP/IP 網絡分層模型是怎樣分層的
- OSI 網絡分層模型是怎樣分層的
- TCP 和 UDP 區別
- HTTP/0.9、HTTP/1.0、HTTP/1.1、HTTP/2、HTTP/3 各版本之間的區別?
- 詳細說說 DNS
- [綜合相關]
- CSRF 跨站請求偽造和 XSS 跨站腳本攻擊是什么?
- 如果讓你去實現一個 CSRF 攻擊你會怎做?
- 如果使用 jsonp 的話會有什么安全問題嗎?
- 你是如何解決跨域的?都有幾種?
- 為什么說 GET 會留下歷史記錄?
- 從“在瀏覽器輸入域名”到“頁面靜態資源完全加載”的整個流程
- 假設有兩個子項目, 他們需要共用同一個用戶體系如何保證關掉頁面之后打開另一個項目用戶還是登錄狀態?
1. HTTP 協議(應用層)
HTTP 的歷史
時間 | 協議 | 特性 |
---|---|---|
1991 | HTTP/0.9 | 只接受 GET 方法,不支持請求頭 |
1996 | HTTP/1 | 基本成型,支付富文本、header、狀態碼、緩存等 |
1999 | HTTP/1.1 | 使用了 20 年的主流標準,支持長連接、管線化、分塊傳輸 |
2009 | SPDY | HTTP/2 前身 |
2013 | QUIC | 基于 UDP 實現 TCP+HTTP/2 并優化 |
2015 | HTTP/2 | 二進制分幀、多路復用、頭部壓縮、服務器推送等 |
2018 | HTTP/3 | QUIC 更名為 HTTP/3 |
1.1 特性
- HTTP 協議構建于 TCP/IP 協議之上的應用層規范
- HTTP 是無狀態無連接的
1.2 HTTP 報文
報文的結構由報文首部、空行、報文主體三部分組, 并不一定要有報文主體。
| 報文首部 | -------- | 空行 | 報文主體 |
請求報文
報文結構 | 格式 | 實例 |
---|---|---|
請求行 | method URL version | POST /admin/v1/platform/user/get HTTP/1.1 |
首部字段 | headers | Content-Type: application/json |
空行(CR+LF) | 空行(CR+LF) | 空行(CR+LF) |
報文主體 | entity-body | {"name":"adi"} |
響應報文
報文結構 | 格式 | 實例 |
---|---|---|
狀態行 | 協議版本 狀態碼 描述短語 | HTTP/1.1 200 OK |
首部字段 | headers | Content-Type: text/html; charset=UTF-8 |
空行(CR+LF) | CR+LF | CR+LF |
報文主體 | entity-body | html... |
首部字段的四種類型以及其他類型
- 通用首部字段
- Cache-Control 控制緩存的行為
- Connection 逐跳首部、連接的管理
- Date 創建報文的日期時間
- Pragma 報文指令
- Trailer 報文末端的首部一覽
- Transfer-Encoding 指定報文主體的傳輸編碼方式
- Upgrade 升級為其他協議
- Via 代理服務器的相關信息
- Warning 錯誤通知
- 請求首部字段
- Accept 用戶代理可處理的媒體類型
- Accept-Charset 優先的字符集
- Accept-Encoding 優先的內容編碼
- Accept-Language 優先的語言(自然語言)
- Authorization Web 認證信息
- Expect 期待服務器的特定行為
- From 用戶的電子郵箱地址
- Host 請求資源所在服務器
- If-Match 比較實體標記(ETag)
- If-Modified-Since 比較資源的更新時間
- If-None-Match 比較實體標記(與 If-Match 相反)
- If-Range 資源未更新時發送實體
- Byte 的范圍請求
- If-Unmodified-Since
- 比較資源的更新時間(與 If-Modified-Since 相反)
- Max-Forwards 最大傳輸逐跳數
- Proxy-Authorization 代理服務器要求客戶端的認證信息
- Range 實體的字節范圍請求
- Referer 對請求中 URI 的原始獲取方 TE 傳輸編碼的優先級
- User-Agent HTTP 客戶端程序的信息
- 響應首部字段
- Accept-Ranges 是否接受字節范圍請求
- Age 推算資源創建經過時間
- ETag 資源的匹配信息
- Location 令客戶端重定向至指定 URI
- Proxy-Authenticate 代理服務器對客戶端的認證信息
- Retry-After 對再次發起請求的時機要求
- Server HTTP 服務器的安裝信息
- Vary 代理服務器緩存的管理信息
- WWW-Authenticate 服務器對客戶端的認證信息
- 實體首部字段
- Allow 資源可支持的 HTTP 方法
- Content-Encoding 實體主體適用的編碼方式
- Content-Language 實體主體的自然語言
- Content-Length 實體主體的大小(單位:字節)
- Content-Location 替代對應資源的 URI
- Content-MD5 實體主體的報文摘要
- Content-Range 實體主體的位置范圍
- Content-Type 實體主體的媒體類型
- Expires 實體主體過期的日期時間
- Last-Modified 資源的最后修改日期時間
- 其他
- 可能包含 HTTP 的 RFC 里未定義的首部(Cookie 等)。
常見狀態碼:
- 200 OK 客戶端請求成功
- 301 Moved Permanently 請求永久重定向
- 302 Moved Temporarily 請求臨時重定向
- 304 Not Modified 文件未修改, 可以直接使用緩存的文件。
- 400 Bad Request 由于客戶端請求有語法錯誤, 不能被服務器所理解。
- 401 Unauthorized 請求未經授權。這個狀態代碼必須和 WWW-Authenticate 報頭域一起使用
- 403 Forbidden 服務器收到請求, 但是拒絕提供服務。服務器通常會在響應正文中給出不提供服務的原因
- 404 Not Found 請求的資源不存在, 例如, 輸入了錯誤的 URL
- 500 Internal Server Error 服務器發生不可預期的錯誤, 導致無法完成客戶端的請求。
- 503 Service Unavailable 服務器當前不能夠處理客戶端的請求, 在一段時間之后, 服務器可能會恢復正常。
POST 提交數據的方式
HTTP 協議中規定 POST 提交的數據必須在 body 部分中, 但是協議沒有規定數據使用哪種編碼方式或者數據格式。
數據發送到服務端后, 服務端根據請求頭中的 content-type 字段獲知請求頭用何種方式編碼, 再使用相應方式解碼。
- application/x-www-form-urlencoded (default)
- multipart/form-data
- text/plain
- text/xml
- application/x-protobuf
- application/json
GET 與 POST 的區別
- GET 在瀏覽器回退時是無害的, 而 POST 會再次提交請求。
- GET 產生的 URL 地址可以被 Bookmark, 而 POST 不可以。
- GET 請求會被瀏覽器主動 cache, 而 POST 不會, 除非手動設置。
- GET 請求只能進行 url 編碼, 而 POST 支持多種編碼方式。
- GET 請求參數會被完整保留在瀏覽器歷史記錄里, 而 POST 中的參數不會被保留。
- GET 請求在 URL 中傳送的參數是有長度限制的, 而 POST 么有。
- 對參數的數據類型, GET 只接受 ASCII 字符, 而 POST 沒有限制。
- GET 比 POST 更不安全, 因為參數直接暴露在 URL 上, 所以不能用來傳遞敏感信息。
- GET 參數通過 URL 傳遞, POST 放在 Request body 中。
1.3 會話
什么是會話?
客戶端打開與服務器的連接發出請求到服務器響應客戶端請求的全過程稱之為會話。為什么需要會話跟蹤?
瀏覽器與服務器之間的通信是通過 HTTP 協議進行通信的, 而 HTTP 協議是”無狀態”的協議, 它不能保存客戶的信息, 即一次響應完成之后連接就斷開了, 下一次的請求需要重新連接, 這樣就需要判斷是否是同一個用戶, 所以才有會話跟蹤技術來實現這種要求。會話跟蹤常用的方法:
URL 重寫
URL(統一資源定位符)是 Web 上特定頁面的地址, URL 重寫的技術就是在 URL 結尾添加一個附加數據以標識該會話,把會話 ID 通過 URL 的信息傳遞過去, 以便在服務器端進行識別不同的用戶。隱藏表單域
將會話 ID 添加到 HTML 表單元素中提交到服務器, 此表單元素并不在客戶端顯示Cookie
Cookie 是 Web 服務器發送給客戶端的一小段信息, 客戶端請求時可以讀取該信息發送到服務器端, 進而進行用戶的識別。對于客戶端的每次請求, 服務器都會將 Cookie 發送到客戶端,在客戶端可以進行保存,以便下次使用。客戶端可以采用兩種方式來保存這個 Cookie 對象, 一種方式是保存在客戶端內存中, 稱為臨時 Cookie, 瀏覽器關閉后這個 Cookie 對象將消失。另外一種方式是保存在客戶機的磁盤上, 稱為永久 Cookie。以后客戶端只要訪問該網站, 就會將這個 Cookie 再次發送到服務器上, 前提是這個 Cookie 在有效期內, 這樣就實現了對客戶的跟蹤。Cookie 是可以被客戶端禁用的。Session
每一個用戶都有一個不同的 session, 各個用戶之間是不能共享的, 是每個用戶所獨享的, 在 session 中可以存放信息。在服務器端會創建一個 session 對象, 產生一個 sessionID 來標識這個 session 對象, 然后將這個 sessionID 放入到 Cookie 中發送到客戶端, 下一次訪問時, sessionID 會發送到服務器, 在服務器端進行識別不同的用戶。Session 的實現依賴于 Cookie, 如果 Cookie 被禁用, 那么 session 也將失效。
1.4 緩存
緩存類別
- 強緩存
- Expires 、 Cache-Control
- 協商緩存
- Last-Modified 、 ETag
緩存字段
Expires:
HTTP/1.0, 值為服務器設定的 GMT 時間(Wed Sep 23 2020 11:17:01 GMT)。
當客戶端時間小于服務器設定的時間時使用緩存。
Cache-Control:
HTTP/1.1, 可選值如下:
請求首部時可用:
可選值 | 說明 | tips |
---|---|---|
no-cache | 在發布緩存副本之前, 強制要求緩存把請求提交給原始服務器進行驗證(協商緩存驗證)。 | 原服務器判斷緩存是新鮮的會返回 304 狀態碼 |
no-store | 緩存不應存儲有關客戶端請求或服務器響應的任何內容, 即不使用任何緩存。 | 直接不緩存 |
max-age=<seconds> | 告知服務器客戶端希望接收一個存在的時間(Age)不大于 delta-seconds 秒的資源 | |
max-stale[=<seconds>] | 表明客戶端愿意接收一個已經過期的資源。可以設置一個可選的秒數, 表示響應不能已經過時超過該給定的時間。 | |
min-fresh=<seconds> | 表示客戶端希望獲取一個能在指定的秒數內保持其最新狀態的響應。 | |
no-transform | 不得對資源進行轉換或轉變。Content-Encoding、Content-Range、Content-Type 等 HTTP 頭不能由代理修改。 | |
only-if-cached | 表明客戶端只接受已緩存的響應, 并且不要向原始服務器檢查是否有更新的拷貝。 | |
cache-extension | 自定義拓展值 |
響應首部時可用:
可選值 | 說明 | tips |
---|---|---|
public | 表明響應可以被任何對象(包括:發送請求的客戶端, 代理服務器, 等等)緩存 | |
private | 表明響應只能被單個用戶緩存, 不能作為共享緩存(即代理服務器不能緩存它)。 | |
no-cache | 在發布緩存副本之前, 強制要求緩存把請求提交給原始服務器進行驗證(協商緩存驗證)。 | 原服務器判斷緩存是新鮮的會返回 304 狀態碼 |
no-store | 緩存不應存儲有關客戶端請求或服務器響應的任何內容, 即不使用任何緩存。 | 直接不緩存 |
only-if-cached | 表明客戶端只接受已緩存的響應, 并且不要向原始服務器檢查是否有更新的拷貝。 | |
must-revalidate | 一旦資源過期(比如已經超過 max-age), 在成功向原始服務器驗證之前, 緩存不能用該資源響應后續請求。 | |
proxy-revalidate | 與 must-revalidate 作用相同, 但它僅適用于共享緩存(例如代理), 并被私有緩存忽略。 | |
max-age | 設置緩存存儲的最大周期, 超過這個時間緩存被認為過期(單位秒)。 | 與 Expires 相反, 時間是相對于請求的時間。 |
s-maxage | 覆蓋 max-age 或者 Expires 頭, 但是僅適用于共享緩存(比如各個代理), 私有緩存會忽略它。 | |
cache-extension | 自定義拓展值 |
Cache-Control 允許自由組合可選值, 例如:
Cache-Control: max-age=3600, must-revalidate
Pragma:
HTTP/1.0, 值為 node-cache
。
該字段會知會客戶端不要對該資源讀緩存, 即每次都得向服務器發一次請求才行。
優先級:
Pragma > Cache-Control > Expires
Last-Modified:
HTTP/1.0, 值為<day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT 格式, 例如:Last-Modified: Wed Sep 23 2020 11:17:01 GMT
對應請求首部字段:
字段名 | 說明 | 值 |
---|---|---|
If-Modified-Since | 該請求首部會傳遞給服務器端進行最后修改時間的比對, 當值一致時返回 304 和響應頭。 | Wed Sep 23 2020 11:17:01 GMT |
If-Unmodified-Since | 告訴服務器, 若 Last-Modified 沒有匹配上(資源在服務端的最后更新時間改變了), 則應當返回 412(Precondition Failed) 給客戶端 | Wed Sep 23 2020 11:17:01 GMT |
ETag:
HTTP/1.1, 值為根據資源計算得出的唯一標志符。
例如: Etag: 5d8c72a5edda8d6a:3239
對應請求首部字段:
字段名 | 說明 | 值 |
---|---|---|
If-None-Match | 該請求首部會傳遞給服務器端進行 ETag 的比對, 當值一致時返回 304 和響應頭。 | 5d8c72a5edda8d6a:3239 |
If-Match | 告訴服務器如果沒有匹配到 ETag, 或者收到了“*”值而當前并沒有該資源實體, 則應當返回 412(Precondition Failed) 狀態碼給客戶端。 | 5d8c72a5edda8d6a:3239 |
緩存頭部對比
頭部 | 優勢和特點 | 劣勢和問題 |
---|---|---|
Expires | 1、HTTP 1.0 產物, 可以在 HTTP 1.0 和 1.1 中使用, 簡單易用。2、以時刻標識失效時間。 | 1、時間是由服務器發送的(UTC), 如果服務器時間和客戶端時間存在不一致, 可能會出現問題。2、存在版本問題, 到期之前的修改客戶端是不可知的。 |
Cache-Control | 1、HTTP 1.1 產物, 以時間間隔標識失效時間, 解決了 Expires 服務器和客戶端相對時間的問題。2、比 Expires 多了很多選項設置。 | 1、HTTP 1.1 才有的內容, 不適用于 HTTP 1.0 。2、存在版本問題, 到期之前的修改客戶端是不可知的。 |
Last-Modified | 1、不存在版本問題, 每次請求都會去服務器進行校驗。服務器對比最后修改時間如果相同則返回 304, 不同返回 200 以及資源內容。 | 1、只要資源修改, 無論內容是否發生實質性的變化, 都會將該資源返回客戶端。例如周期性重寫, 這種情況下該資源包含的數據實際上一樣的。2、以時刻作為標識, 無法識別一秒內進行多次修改的情況。3、某些服務器不能精確的得到文件的最后修改時間。 |
ETag | 1、可以更加精確的判斷資源是否被修改, 可以識別一秒內多次修改的情況。2、不存在版本問題, 每次請求都回去服務器進行校驗。 | 1、計算 ETag 值需要性能損耗。2、分布式服務器存儲的情況下, 計算 ETag 的算法如果不一樣, 會導致瀏覽器從一臺服務器上獲得頁面內容后到另外一臺服務器上進行驗證時發現 ETag 不匹配的情況。 |
1.5 缺點
- 通信使用明文(不加密), 內容可能會被竊聽(TCP/IP)
- 不驗證通信方的身份, 因此有可能遭遇偽裝(客戶端和服務端都有可能)
- 無法證明報文的完整性, 有可能會被篡改(中間人攻擊)
1.6 HTTP/1.1
長連接
特性:
- 我們知道 HTTP 協議采用“請求-應答”模式,每個請求/應答客戶和服務器都要新建一個連接, 完成之后立即斷開連接(HTTP 協議無連接特性);
- 當使用了 Keep-Alive 模式(長連接)時, 數據傳輸完成了保持 TCP 連接不斷開(不發 RST 包、不四次握手), 使客戶端到服務端的鏈接持續有效;
歷史:
- 在 HTTP/1.0 中, 默認使用短連接, 提出長連接(也叫持久連接)的概念, 但當時僅提供初步的支持。
- 在 HTTP/1.1 中, 默認使用長連接, 用以保持連接特性。使用長連接的 HTTP 協議, 會在響應頭加入這行代碼:Connection:keep-alive, 只有設置 Connection: close 才會關閉。
長連接短連接操作過程:
- 短連接: 建立連接 -> 數據傳輸 -> 關閉連接 , 建立連接 -> 數據傳輸 -> 關閉連接
- 長連接: 建立連接 -> 數據傳輸...(保持連接)...數據傳輸 -> 關閉連接
長連接過期時間:
- 設置 keep-Alive: timeout=20 , max = 10。 timeout=20, 表示這個 TCP 通道可以保持 20 秒, max=10, 表示這個長連接最多接收 10 次請求就斷開。
識別傳輸結束:
- 判斷傳輸數據是否達到了 Content-Length 指示的大小;
- 動態生成的文件沒有 Content-Length , 它是分塊傳輸(chunked), 這時候就要根據 chunked 編碼來判斷, chunked 編碼的數據在最后有一個空 chunked 塊, 表明本次傳輸數據結束。
長連接與 WebSocket 的區別:
- HTTP 的長連接:HTTP/1.1 通過使用 Connection:keep-alive 進行長連接。在一次 TCP 連接中可以完成多個 HTTP 請求, 但是對每個請求仍然要單獨發 header, Keep-Alive 不會永久保持連接, 它有一個保持時間, 可以在不同的服務器軟件(如 Apache)中設定這個時間。這種長連接是一種“偽鏈接”, 而且只能由客戶端發送請求, 服務端響應。
- WebSocket 的長連接, 是一個全雙工的連接, 可由服務端主動發起信息。長連接第一次 TCP 鏈路建立之后, 后續數據可以雙方都進行發送, 不需要發送請求頭。
- HTTP/1.1 中雙方并沒有建立正真的連接會話, 服務端可以在任何一次請求完成后關閉。WebSocket 它本身就規定了是正真的、雙工的長連接, 兩邊都必須要維持住連接的狀態。
管線化
特性:
- 在長連接的基礎上, 將多個 HTTP 請求并行發送, 在發送過程中不用等待響應, 解決了長連接中某個請求的響應沒有及時返回導致后續請求無法發起的 HTTP 隊頭阻塞問題。
過程:
- 長連接:請求 1 -> 響應 1 -> 請求 2 -> 響應 2 -> 請求 3 -> 響應 3
- 管線化:請求 1 -> 請求 2 -> 請求 3 -> 響應 1 -> 響應 2 -> 響應 3
限制:
- 初次創建連接不能啟用管線機制
- 只有 GET 和 HEAD 請求可以進行管線化, 而 POST 則有所限制。
- 客戶端和服務器端都要支持 HTTP/1.1 協議。
- HTTP /1.1 要求服務器端支持管線化, 但并不要求服務器端也對響應進行管線化處理, 只是要求對于管線化的請求不失敗即可
- 由于上面提到的服務器端問題, 開啟管線化很可能并不會帶來大幅度的性能提升, 而且很多服務器端和代理程序對管線化的支持并不好, 因此現代瀏覽器如 Chrome 和 Firefox 默認并未開啟管線化支持
編碼提升傳輸速率
報文主體和實體主體的差異
在開始學習編碼傳輸前, 我們需要了解 報文主體 和 實體主體。
報文主體是 HTTP 報文(報文頭部、空行和報文主體)的一部分。
實體主體是由實體首部和實體主體組成, 作為請求或響應的有效載荷數據被傳輸。
通常, 報文主體等于實體主體。只有當傳輸中進行編碼操作時, 實體主體的內容發生變化, 才導致它和報文主體產生差異。(tips: 這一塊不好理解的話, 可以看看標題的 link)
傳輸內容編碼
在傳輸數據時可以按照數據原貌直接傳輸, 但也可以在傳輸過程中通過編碼提升傳輸速率。通過在傳輸時編碼, 能有效地處理大量的訪問請求。
過程:
- 客戶端設置 Accept-Encoding 字段, 告知服務端當前支持的壓縮編碼。
- 服務端進行選擇一種客戶端支持的編碼方式進行編碼, 編碼后在響應頭添加 Content-Encoding 字段, 告知客戶端使用哪種方式解碼, 并修改響應頭中的 Content-Length 告知實體編碼后的長度。
常用的編碼類型:
- gzip:表明實體采用 GNU zip 編碼。
- compress:表明實體采用 Unix 的文件壓縮程序。
- deflate:表明使用是用 zlib 的格式壓縮的。
- br:表明實體使用 Brotli 算法的壓縮格式。
- identity:表明沒有對實體進行編碼, 為默認值。
在這些編碼類型中, 除了 identity 之外, 都是無損壓縮, 他們都是需要可還原成原始的文本內容的。gzip 通常是效率最高的, 使用最廣泛的。
分塊傳輸編碼
- 傳輸編碼必須配合持久連接去使用, 為了在一個持久連接中, 將數據分塊傳輸, 并標記傳輸結束而設計的.
- 傳輸編碼使用 Transfer-Encoding 首部進行標記, 在最新的 HTTP/1.1 協議里, 它只有 chunked 這一個取值, 表示分塊編碼。
- 分塊的格式:數據長度(16 進制)+ 分塊數據。
- 如果還有額外的數據, 可以在結束之后, 使用 Trailer 進行拖掛傳輸額外的數據。
分塊傳輸的規則:
- 每個分塊包含一個 16 進制的數據長度值和真實數據。
- 數據長度值獨占一行, 和真實數據通過 CRLF(\r\n) 分割。
- 數據長度值, 不計算真實數據末尾的 CRLF, 只計算當前傳輸塊的數據長度。
- 最后通過一個數據長度值為 0 的分塊, 來標記當前內容實體傳輸結束。
內容編碼和傳輸編碼結合
內容編碼和傳輸編碼一般都是配合使用的。
我們會先使用內容編碼, 將內容實體進行壓縮, 然后再通過傳輸編碼分塊發送出去。
客戶端接收到分塊的數據, 再將數據進行重新整合, 還原成最初的數據。
1.7 HTTP/2.0
HTTP/1.1 存在的問題:
1、TCP 連接數限制
同一域名, 瀏覽器限制了最多同時創建 6-8 個 TCP 連接, 當請求數過多時會被瀏覽器掛起等待。
2、線頭阻塞問題
HTTP/1.0 時, 一個 TCP 連接只能處理一個請求;HTTP/1.1 默認開啟長連接, 使得一個 TCP 連接可以處理多個請求, 它要求以 請求 1 -> 響應 1 -> 請求 2 -> 響應 2 順序進行通訊, 當 請求 1 請求時間較長會阻塞住 請求, 造成 HTTP 隊頭阻塞問題。 2。于是出現了管線化技術, 它可以并發多個請求然后按照順序返回響應, 請求 3 && 請求 4 -> 響應 3 && 響應 4, 不過管線化也沒有很好的解決響應阻塞的問題, 當 響應 3 響應時間較長會阻塞住 響應 4 , 造成 HTTP 隊頭阻塞問題。
3、Header 體積優化問題
HTTP/1.x 每次通信都會攜帶一組頭部, 用于描述這次通信的的資源、瀏覽器屬性、cookie 等, 都會攜帶大量冗余頭信息, 并且每次請求 header 基本不怎么變化, 尤其在移動端增加用戶流量,在一定程度上增加了傳輸的成本。
4、明文傳輸的安全問題
HTTP1.x 在傳輸數據時, 所有傳輸的內容都是明文的, 存在網絡竊聽、篡改、冒充等風險。
SPDY 協議
對于 HTTP/1.x 存在的問題, 那時的前端們會使用雪碧圖、域名分片等手法來優化用戶體驗。這些技巧一定程度上優化了用戶體驗, 但 HTTP 協議本身的限制也令人有些束手無策。為了進行根本性的改善, 需要有一些協議層面上的改動, 于是谷歌推出 SPDY 協議。
使用 SPDY 后, HTTP 協議等到了一下功能: 多路復用流, 壓縮 HTTP 首部, 賦予請求優先級, 服務器推送, 服務器提示 等等, SPDY 的實踐證明了這些優化的效果, 也最終帶來 HTTP/2 的誕生。
二進制分幀層
HTTP/2 采用二進制格式傳輸數據, HTTP 1.x 使用的是文本格式, 二進制協議解析起來更高效。
概念:
- 流:流是連接中的一個虛擬信道, 可以承載雙向的消息;每個流都有一個唯一的整數標識符(1、2…N)
- 幀:HTTP/2 數據通信的最小單位消息:指 HTTP/2 中邏輯上的 HTTP 消息。例如請求和響應等, 消息由一個或多個幀組成。
HTTP/2 中, 同域名下所有通信都在單個連接上完成, 該連接可以承載任意數量的雙向數據流。每個數據流都以消息的形式發送, 而消息又由一個或多個幀組成。多個幀之間可以亂序發送, 根據幀首部的流標識(identifier)可以重新組裝。
多路復用
在 HTTP 1.x 中, 如果想并發多個請求, 必須使用多個 TCP 鏈接。
在 HTTP/2 中, 有了二進制分幀之后, HTTP /2 不再依賴 TCP 鏈接去實現多流并行。
特性:
- 同域名下所有通信都在單個連接上完成
- 單個連接可以承載任意數量的雙向數據流
- 數據流以消息的形式發送, 而消息又由一個或多個幀組成, 多個幀之間可以亂序發送, 因為根據幀首部的流標識可以重新組裝
服務端推送
瀏覽器發送一個請求, 服務器主動向瀏覽器響應并推送與這個請求相關的資源。
服務端推送主要針對資源內聯進行優化。例如客戶端請求獲取 index.html,里面有內聯的 index.css, , 這時服務器接收到 index.html 請求后, 會返回 index.html 和 index.css 給客戶端, 客戶端在解析 html 時就不需要在發送這些請求了。
服務端可以主動推送, 客戶端也有權利選擇是否接收。如果服務端推送的資源已經被瀏覽器緩存過, 瀏覽器可以通過發送 RST_STREAM 幀來拒收。主動推送也遵守同源策略, 換句話說, 服務器不能隨便將第三方資源推送給客戶端, 而必須是經過雙方確認才行。
Header 壓縮
HTTP/2 使用 HPACK 算法來壓縮首部內容, 能夠節省消息頭占用的網絡的流量。
HTTP 在每次通信是都會攜帶一組頭部,大部分請求頭變化不大(ua、cookie)。
為了減少這塊的資源消耗并提升性能, HTTP/2 對這些首部采用了壓縮策略:
- HTTP/2 在客戶端和服務器端使用“首部表”來跟蹤和存儲之前發送的鍵-值對,對于相同的數據,不再通過每次請求和響應發送;
- 首部表在 HTTP/2 的連接存續期內始終存在,由客戶端和服務器共同漸進地更新;
-
每個新的首部鍵-值對要么被追加到當前表的末尾,要么替換表中之前的值。
例如:下圖中的兩個請求, 請求一發送了所有的頭部字段,第二個請求則只需要發送差異數據,這樣可以減少冗余數據,降低開銷。
首部表的應用
應用層的重置連接
HTTP/1 通過設置 tcp segment 里的 reset flag 來通知對端關閉連接的。這種方式會直接斷開連接,下次再發請求就必須重新建立連接。HTTP/2 引入 RST_STREAM 類型的 frame,可以在不斷開連接的前提下取消某個 request 的 stream,表現更好。
流量控制
TCP 協議通過 sliding window 的算法來做流量控制。發送方有個 sending window,接收方有 receive window。HTTP/2 的 flow control 是類似 receive window 的做法,數據的接收方通過告知對方自己的 flow window 大小表明自己還能接收多少數據。只有 Data 類型的 frame 才有 flow control 的功能。對于 flow control,如果接收方在 flow window 為零的情況下依然更多的 frame,則會返回 block 類型的 frame,這張場景一般表明 HTTP/2 的部署出了問題。
更安全的 SSL
HTTP/2 使用了 tls 的拓展 ALPN 來做協議升級,除此之外加密這塊還有一個改動,HTTP/2 對 tls 的安全性做了近一步加強,通過黑名單機制禁用了幾百種不再安全的加密算法,一些加密算法可能還在被繼續使用。如果在 ssl 協商過程當中,客戶端和 server 的 cipher suite 沒有交集,直接就會導致協商失敗,從而請求失敗。在 server 端部署 HTTP/2 的時候要特別注意這一點。
升級 HTTP/2 后哪些優化技巧可以棄用了?
- 雪碧圖
- 域名分片
- 內聯資源
HTTP/2 的缺陷
- TCP 以及 TCP+TLS 建立連接的延時
- TCP 三次握手、TLS 四次握手
- 隊頭阻塞并沒有徹底解決 - TCP 隊頭阻塞
- HTTP/2 底層使用的是 TCP 協議, TCP 是一個按序傳輸的鏈條, 一旦傳輸雙方網絡中有一個數據包丟失, 或者網絡出現中斷, 整個 TCP 連接就會暫停, 丟失的數據包需要重新傳輸。
- 多路復用導致服務器壓力上升
- 多路復用沒有限制同時請求數。請求的平均數量與往常相同, 但實際會有許多請求的短暫爆發, 導致瞬時 QPS 暴增。
Reference
一文讀懂 HTTP/2 特性
一文讀懂 HTTP/2 及 HTTP/3 特性
HTTP/2 和 HTTP/1 速度對比
再談 HTTP/2 性能提升之背后原理—HTTP2 歷史解剖
HTTP/2 詳解
硬核!30 張圖解 HTTP 常見的面試題
1.8 HTTP/3.0
HTTP/3 是由 Google 開發協議 QUIC 改名而來。
RTT(Round-Trip Time):
往返時延。表示從發送端發送數據開始, 到發送端收到來自接收端的確認(接收端收到數據后便立即發送確認), 總共經歷的時延。
QUIC 協議
QUIC 是基于 UDP 協議二次開發的協議,它整合了 TCP、TLS、HTTP/2 的優點并加以優化。
特性:
- 零 RTT 建立連接
- 連接遷移
- 隊頭阻塞/多路復用
- 擁塞控制
- 熱插拔
- 前向糾錯 FEC
- 單調遞增的 Packet Number
- ACK Delay
- 流量控制
HTTP/3 的缺點
Reference
http3-explained
HTTP/3 原理與實踐
深入解讀 HTTP3 的原理及應用
https://www.jesuisundev.com/en/understand-http3-in-5-minutes
白話 http 隊頭阻塞
一文讀懂 HTTP/1HTTP/2HTTP/3
1.9 HTTPS
HTTPS (HTTP Secure, 超文本傳輸安全協議) = HTTP + SSL/TLS(加密 ,認證 ,完整性驗證)。
作用
HTTP 使用明文在網絡進行傳輸, 帶來了三大風險:
- 竊聽風險: 第三方可以獲知通信內容。
- 篡改風險: 第三方可以修改通信內容。
- 冒充風險: 第三方可以冒充他人身份參與通信。
HTTPS 通過加入安全套接層(SSL/TLS), 做到了: - 防竊聽: 所有信息都是加密傳播, 第三方無法竊聽。
- 防篡改: 具有校驗機制, 一旦被篡改, 通信雙方會立刻發現。
- 防冒充: 配備身份證書, 防止身份被冒充。
SSL 與 TLS 協議
- SSL(Secure Socket Layer, 安全套接層)
- TLS(Transport Layer Security, 安全層傳輸協議)
SSL/TLS 建立在 TCP 協議之上, 在使用前需要與服務器進行三次握手建立連接。
步驟:
- TCP 三次握手
- SSL/TLS 四次握手
- 傳輸數據
- TCP 四次揮手
tips: TCP 三次握手與四次揮手在后面章節有詳細介紹
SL/TLS 四次握手 基本過程
步驟:
- 客戶端向服務器端索要證書取出公鑰(非對稱加密)并驗證
- 雙方協商生成"對話密鑰"(對稱加密)
- 雙方采用"對話密鑰"進行加密通信
SSL/TLS 四次握手 詳細過程
步驟:
- TLS CLient: 生成第一個隨機數, 發起要求加密通信的請求, 攜帶( 1.支持的協議版本 2.第一個隨機數 3.支持的加密方法 4.支持的壓縮方法 [5.請求的域名] )
- TLS Server: 生成第二個隨機數, 回應客戶端, 返回( 1.確定使用的協議版本 2.第二個隨機數 3.確定使用的加密方法 4. 服務器證書 [5.是否需要客戶端證書校驗身份] )
- TLS CLient: 生成第三個隨機數, 驗證服務器返回的證書, 取出 服務器公鑰 (非對稱加密), 根據三個隨機數計算出"對話密鑰"保存。隨后使用 服務器公鑰 加密數據后發起請求, 請求攜帶( 1.第三個隨機數 2.編碼改變通知,表示隨后通話將使用對話秘鑰加密數據 3.握手結束通知,標識客戶端握手結束,同同時返回前面所有內容的 hash, 用來供服務器校驗 )
- TLS Server: 服務器接收到數據后用私鑰解密獲取客戶端攜帶的數據, 根據三個隨機數計算出"對話密鑰"。回應客戶端, 返回( 1.編碼改變通知, 表示隨后的信息都將用雙方商定的加密方法和密鑰發送 2.服務器握手結束通知, 表示服務器的握手階段已經結束。這一項同時也是前面發送的所有內容的 hash 值, 用來供客戶端校驗)
tips:
- TLS CLient: 客戶端請求
- TLS Server: 服務端響應
- 中括號的內容是可選的
- 以上說明的都是 TLS 1.2 協議的握手情況, 在 1.3 協議中, 首次建立連接只需要一個 RTT, 后面恢復連接不需要 RTT 了
TLS/1.3
TLS/1.3 相對 TLS/1.2 在安全與性能方面進行了一系列改進。
減少握手等待時間,將握手時間從 2-RTT 降低到 1-RTT,并且增加 0-RTT 模式。
1-RTT:
第 1 步,客戶端發送 ClientHello 消息,該消息主要包括客戶端支持的協議版本、會話 ID、密碼套件、壓縮算法、以及擴展消息(密鑰共享、預共享密鑰、預共享密鑰模式);
第 2 步,服務端回復 ServerHello,包含選定的加密套件;發送證書給客戶端;使用證書對應的私鑰對握手消息簽名,將結果發送給客戶端;選用客戶端提供的參數生成臨時公鑰,結合選定的參數計算出用于加密 HTTP 消息的共享密鑰;服務端生成的臨時公鑰通過 KeyShare 消息發送給客戶端;
第 3 步,客戶端接收到 KeyShare 消息后,使用證書公鑰進行簽名驗證,獲取服務器端的臨時公鑰,生成會話所需要的共享密鑰;
第 4 步,雙方使用生成的共享密鑰對消息加密傳輸,保證消息安全。
0-RTT:
TLS 1.3 的客戶端連接到同樣支持 TLS 1.3 的服務器時, 客戶端會將收到服務器發送過來的 Ticket 通過相關計算,一起組成新的 預共享密鑰,PSK (PreSharedKey)。客戶端會將該 PSK 緩存在本地,在會話恢復時在 Client Hello 上帶上 PSK 擴展,同時通過之前客戶端發送的完成(finished)計算出恢復密鑰 (Resumption Secret)通過該密鑰加密數據發送給服務器。服務器會從會話 Ticket 中算出 PSK,使用它來解密剛才發過來的加密數據。至此完成了該 0-RTT 會話恢復的過程。
SSL 與 TLS 歷史
協議/版本 | 說明 |
---|---|
SSL/1.0 | 1994 年, NetScape 公司設計了 SSL/1.0, 但是沒發布 |
SSL/2.0 | 1995 年, NetScape 公司發布 SSL 2.0 版, 很快發現有嚴重漏洞 |
SSL/3.0 | 1996 年, SSL 3.0 版問世, 得到大規模應用 |
TLS/1.0 | 1999 年, 互聯網標準化組織 ISOC 接替 NetScape 公司, 發布了 SSL 的升級版 TLS 1.0 版 |
TLS/1.1 | 2006 年, ISOC 發布升級版 |
TLS/1.3 | 2008 年, ISOC 發布修訂版 |
目前, 當前廣泛使用的版本是 SSL3.0 和 TLS1.0。主流瀏覽器都已經實現了 TLS 1.2 的支持。
HTTPS 比 HTTP 慢?慢多少?
HTTPS 在使用 SSL 時它的速度會變慢。
SSL 的慢分兩種。一種是指通信慢。另一種是指由于大量消耗 CPU 及內存等資源, 導致處理速度變慢。 和使用 HTTP 相比, 網絡負載可能會變慢 2 到 100 倍。除去和 TCP 連接、發送 HTTP 請求 ? 響應以外, 還必須進行 SSL 通信, 因此整體上處理通信量不可避免會增加。 另一點是 SSL 必須進行加密處理。在服務器和客戶端都需要進行 加密和解密的運算處理。因此從結果上講, 比起 HTTP 會更多地 消耗服務器和客戶端的硬件資源, 導致負載增強。
針對速度變慢這一問題, 并沒有根本性的解決方案, 我們會使用 SSL 加速器這種(專用服務器)硬件來改善該問題。該硬件為 SSL 通信專用硬件, 相對軟件來講, 能夠提高數倍 SSL 的計算速 度。僅在 SSL 處理時發揮 SSL 加速器的功效, 以分擔負載。
Reference
- 阮一峰 SSL/TLS 協議
- 圖解 SSL/TLS 協議
- 鴿子傳信解釋 HTTPS
- 圖解 HTTPS:Charles 捕獲 HTTPS 的原理
- 看完還不懂 HTTPS 我直播**
- 免費申請 HTTPS 證書, 開啟全站 HTTPS
2. DNS 協議(會話層)
DNS( Domain Name System)是“域名系統”的英文縮寫,是一種組織成域層次結構的計算機和網絡服務命名系統,它用于 TCP/IP 網絡,它所提供的服務是用來將主機名和域名轉換為 IP 地址的工作。
2.1 DNS 查找過程
域名 -> 檢查瀏覽器緩存 -> 檢查系統 Hosts 文件 DNS 緩存 -> 檢查路由器緩存 -> 檢查 ISP(互聯網服務提供商)DNS 緩存 -> 根域名服務器 -> 頂級域名服務器 -> 權威域名服務器
以步驟中當檢查到 ip 時將終止向后查詢的步驟并保存結果至緩存。
2.2 遞歸查詢和迭代查詢
遞歸查詢:如果主機所詢問的本地域名服務器不知道被查詢域名的 IP 地址,那么本地域名服務器就以 DNS 客戶的身份,向其他根域名服務器繼續發出查詢請求報文,而不是讓該主機自己進行下一步的查詢。
迭代查詢:當根域名服務器收到本地域名服務器發出的迭代查詢請求報文時,要么給出所要查詢的 IP 地址,要么告訴本地域名服務器:你下一步應當向哪一個域名服務器進行查詢。然后讓本地域名服務器進行后續的查詢,而不是替本地域名服務器進行后續的查詢。
由此可見,客戶端到 Local DNS 服務器,Local DNS 與上級 DNS 服務器之間屬于遞歸查詢;DNS 服務器與根 DNS 服務器之前屬于迭代查詢。
2.3 HTTPDNS
HTTPDNS 使用 HTTP 與 DNS 服務器交互,代替傳統的基于 UDP 的 DNS 協議,域名解析請求直接發送到 HTTPDNS 服務端,從而繞過運營商的 Local DNS,防止 DNS 劫持。
Reference
3. TCP 協議(傳輸層)
TCP 協議 是以太網協議和 IP 協議的上層協議, 也是應用層協議的下層協議。
TCP 協議 全稱是傳輸控制協議是一種 ** 面向連接的、可靠的、基于字節流的傳輸層通信協議 **, 由 IETF 的 RFC 793 定義。TCP 是面向連接的、可靠的流協議。
3.1 OSI 網絡模型(OSI 七層網絡模型、TCP/IP 四層概念模型、五層協議的體系結構)
OSI 七層網絡模型 | TCP/IP 四層概念模型 | 五層協議的體系結構 | 對應網絡協議 |
---|---|---|---|
7. 應用層 | 4. 應用層 | 5.應用層 | HTTP、TFP、NFS、TFTP、WAIS、SMTP |
6. 表示層 | ^ | ^ | Telnet、Rlogin、SNMP、Gopher |
5. 會話層 | ^ | ^ | DNS、SMTP |
4. 傳輸層 | 3. 傳輸層 | 4. 傳輸層 | TCP、UDP |
3. 網絡層 | 2. 網際層 IP | 3. 網絡層 | IP、ICMP、ARP、RARP、AKP、UUCP |
2. 數據鏈路層 | 1. 網絡接口層 | 2. 數據鏈路層 | Ethernet、FDDI、Arpanet、PDN、SLIP、PPP |
1. 物理層 | ^ | 1. 物理層 | IEEE 802.1A、IEEE 802 - IEEE 802.11 |
- Tips:
- ^ 表示向上合并單元格
- TCP/IP 是廣泛使用的模型, 分四層。osi 模型是理論下的國際標準模型, 分七層。五層模型是綜合了 OSI 和 TCP/IP 模型得到的五層模型, 為方便學習計算機網絡原理而采用。
3.2 TCP 數據包大小
以太網數據包大小為 1522 字節, 其中 22 字節是頭信息, 1500 字節是負載。
IP 數據包在以太網數據包負載里面, IP 數據包頭信息也需要最少 20 字節, 負載最多為 1460 字節。
TCP 數據包在 IP 數據包中, TCP 頭信息也有額外的頭信息, 所有 TCP 負載實際在 1400 字節左右。
3.3 TCP 報文首部字段注析
| 字段 | 字段名 | 縮寫 | 作用 |
| 應答響應 | Acknowledgment | ACK | 表示確認序號有效。確認應答的字段有效。TCP 規定除了最初建立連接時的 SYN 包之外該位必須設置為 1。 |
| 同步 | Synchronize | SYN | 用來發起一個連接,建立連接。SYN 為 1 表示希望建立連接。 |
| 結束 | Finish | FIN | 結束會話,關閉連接。表示發送方完成任務,今后不會有數據發送,希望斷開連接。 |
| 初始序列號 | Initial Sequence Number | ISN | 一個隨機數,在三次握手的過程當中,使用 seq 字段交換雙方的 ISN。 |
| 序列號 | Sequence Number | seq | 指的是本報文段第一個字節的序列號。 |
| 確認應答號 | Acknowledgement Number | ack(ACK number) | 用來告知對方下一個期望接收的序列號,小于 ack 的所有字節已經全部收到。 |
3.4 三次握手
- 最初客戶端和服務端都處于 CLOSED(關閉) 狀態。
- TCP 服務器進程先創建傳輸控制塊 TCB, 時刻準備接受客戶進程的連接請求, 此時服務器就進入了 LISTEN(監聽)狀態;
- TCP 客戶進程也是先創建傳輸控制塊 TCB, 然后向服務器發出連接請求報文, 這是報文首部中的同部位 SYN=1, 同時選擇一個初始序列號 seq=x , 此時, TCP 客戶端進程進入了 SYN-SENT(同步已發送狀態)狀態。TCP 規定, SYN 報文段(SYN=1 的報文段)不能攜帶數據, 但需要消耗掉一個序號。
- TCP 服務器收到請求報文后, 如果同意連接, 則發出確認報文。確認報文中應該 ACK=1, SYN=1, 確認號是 ack=x+1, 同時也要為自己初始化一個序列號 seq=y, 此時, TCP 服務器進程進入了 SYN-RCVD(同步收到)狀態。這個報文也不能攜帶數據, 但是同樣要消耗一個序號。
- TCP 客戶進程收到確認后, 需要向服務器發送確認消息。確認報文的 ACK=1, ack=y+1, 自己的序列號 seq=x+1, 此時, TCP 連接建立, 客戶端進入 ESTABLISHED(已建立連接)狀態。TCP 規定, ACK 報文段可以攜帶數據, 如果不攜帶數據不消耗序號。
- 當服務器收到客戶端的確認后也進入 ESTABLISHED(已建立連接)狀態, 此后雙方就可以開始通信了。
3.5 四次揮手
- 數據傳輸完畢后, 雙方都可主動釋放連接。
- 客戶端進程發出連接釋放報文, 并且停止發送數據。釋放數據報文首部, FIN=1, 其序列號為 seq=u(等于前面已經傳送過來的數據的最后一個字節的序號加 1), 此時, 客戶端進入 FIN-WAIT-1(終止等待 1)狀態。 TCP 規定, FIN 報文段即使不攜帶數據, 也要消耗一個序號。
- 服務器收到連接釋放報文, 發出確認報文, ACK=1, ack=u+1, 并且帶上自己的序列號 seq=v, 此時, 服務端就進入了 CLOSE-WAIT(關閉等待)狀態。TCP 服務器通知高層的應用進程, 客戶端向服務器的方向就釋放了, 這時候處于半關閉狀態, 即客戶端已經沒有數據要發送了, 但是服務器若發送數據, 客戶端依然要接受。這個狀態還要持續一段時間, 也就是整個 CLOSE-WAIT 狀態持續的時間。
- 客戶端收到服務器的確認請求后, 此時, 客戶端就進入 FIN-WAIT-2(終止等待 2)狀態, 等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最后的數據)。
- 服務器將最后的數據發送完畢后, 就向客戶端發送連接釋放報文, FIN=1, ack=u+1, 由于在半關閉狀態, 服務器很可能又發送了一些數據, 假定此時的序列號為 seq=w, 此時, 服務器就進入了 LAST-ACK(最后確認)狀態, 等待客戶端的確認。
- 客戶端收到服務器的連接釋放報文后, 必須發出確認, ACK=1, ack=w+1, 而自己的序列號是 seq=u+1, 此時, 客戶端就進入了 TIME-WAIT(時間等待)狀態。注意此時 TCP 連接還沒有釋放, 必須經過 2MSL(最長報文段壽命)的時間后, 當客戶端撤銷相應的 TCB 后, 才進入 CLOSED 狀態。
- 服務器只要收到了客戶端發出的確認, 立即進入 CLOSED 狀態。同樣, 撤銷 TCB 后, 就結束了這次的 TCP 連接。服務器結束 TCP 連接的時間要比客戶端早一些。
等待 2MSL 的意義
2MSL(Maximum Segment Lifetime,報文最大生存時間)
- 保證客戶端發送的最后一個 ACK 報文段能夠到達服務端。
這個 ACK 報文段有可能丟失,使得處于 LAST-ACK 狀態的 B 收不到對已發送的 FIN+ACK 報文段的確認,服務端會進行超時重傳 FIN+ACK 報文段,而客戶端能在 2MSL 時間內收到這個重傳的 FIN+ACK 報文段,接著客戶端重傳一次確認,重新啟動 2MSL 計時器,最后客戶端和服務端都進入到 CLOSED 狀態,若客戶端在 TIME-WAIT 狀態不等待一段時間,而是發送完 ACK 報文段后立即釋放連接,則無法收到服務端重傳的 FIN+ACK 報文段,所以不會再發送一次確認報文段,則服務端無法正常進入到 CLOSED 狀態。 - 防止已失效的連接請求報文段出現在本連接中。
客戶端在發送完最后一個 ACK 報文段后,再經過 2MSL,就可以使本連接持續的時間內所產生的所有報文段都從網絡中消失,使下一個新的連接中不會出現這種舊的連接請求報文段。
為什么是四次揮手而不是三次?
當服務器執行第二次揮手之后, 此時證明客戶端不會再向服務端請求任何數據, 但是服務端可能還正在給客戶端發送數據(可能是客戶端上一次請求的資源還沒有發送完畢),所以此時服務端會等待把之前未傳輸完的數據傳輸完畢之后再發送關閉請求。
3.6 重傳機制
1、超時重傳
當發送數據包在超時重傳時間 RTO(Retransmission Timeout)內沒有收到響應的 ack,認為這個數據包丟失,就會重新發送數據包。
2、快速重傳
當發送方連續收到 3 次相同的 ack,這個時候即使超時定時器還沒有超時,也會進行啟動重傳。
重傳數據包處理:
當觸發重傳機制時,應該只重傳丟失的報文還是應該重傳丟失的報文以及序號在后面的報文(前面已發送的報文)?
為了解決不知道該重傳哪些報文的問題,于是有了 SACK 方法。
- 選擇性確認 SACK( Selective Acknowledgment )
在 TCP 頭部中加入一個 SACK 字段,用來記錄已收到的報文序號,知道了已收到的序號就可以做到只重傳丟失的報文。
3.7 滑動窗口
滑動窗口協議是傳輸層進行流控的一種措施,接收方通過通告發送方自己的窗口大小,從而控制發送方的發送速度,從而達到防止發送方發送速度過快而導致自己被淹沒的目的。TCP 的滑動窗口解決了端到端的流量控制問題,允許接受方對傳輸進行限制,直到它擁有足夠的緩沖空間來容納更多的數據。
3.8 擁塞控制
擁塞控制協議是作用于網絡的,防止過多的數據注入到網絡中,避免出現網絡負載過大的情況。
擁塞控制算法:
- 慢開始
發送方維持一個叫做擁塞窗口 cwnd(congestion window)的狀態變量。擁塞窗口的大小取決于網絡的擁塞程度,并且動態地在變化,一開始先發送少量的數據而后取決于網絡堵塞情況根據算法動態調整擁塞窗口大小。慢開始的發包個數是指數性的增長。 - 擁塞避免
當 cwnd 大于等于慢啟動門限 ssthresh(slow start threshold)時,將使用擁塞避免算法。擁塞避免算法讓擁塞窗口緩慢增長,即每經過一個往返時間 RTT 就把發送方的擁塞窗口 cwnd 加 1,而不是加倍。這樣擁塞窗口按線性規律緩慢增長。
擁塞避免的發包個數是線性的增長。 - 擁塞發生
當網絡進入擁塞狀況時,會出現丟包現象,這時就需要對丟失的數據包進行重傳(超時重傳或快速重傳)。當觸發超時重傳時會使用擁塞發生算法。
擁塞發生算法會將 ssthresh 設為 cwnd/2, cwnd 重置為 1,隨后重新開始慢啟動。 - 快速恢復
當觸發快速重傳機制時,TCP 認為這種情況不嚴重,會將 cwnd 設為 cwnd/2, ssthresh 設為 cwnd , 隨后進入快速恢復算法。
快速恢復算法會將 cwnd 設為 ssthresh + 3 (3 個 ASK 報文) , 然后重傳丟失的數據包,如果再收到重復的 ACK,那么 cwnd 增加 1, 如果收到新數據的 ACK 后會將 cwnd 設置 為 ssthresh,接著就進入了擁塞避免算法。
3.9 快速打開(TFO)
開啟 TFO 功能
- 正常進行三次握手建立連接,在第一次握手的時候添加 Fast Open 選項,請求打開 TFO。
- 支持 TFO 功能的服務器生成 Cookie,并通過第二次握手中的 Fast Open 字段返回給客戶端。
- 客戶端收到 SYN-ACK 后,緩存 Fast Open 選項中的 Cookie。
** 使用 TFO 功能 ** - 客戶端發起第一次握手包,數據包中包含 Cookie、SYN 和數據(例如 HTTP 請求)。
- 服務器接收到 Cookie 并進行校驗: 如果 Cookie 有效,服務器將在 SYN-ACK 數據包中對 SYN 和數據進行確認,隨后將響應數據發送給客戶端。如果 Cookie 無效,服務器將丟棄數據包中的數據,隨后只發送 SYN-ACK 數據包進行握手確認。
- 客戶端發送 ACK 確認服務器返回的 SYN 以及數據, 如果客戶端在初始化 SYN 數據包的數據沒有被服務器端確認,客戶端將重發該數據。
3.10 TCP keep-alive 機制
通訊雙方建立起連接,如果通訊雙方沒有發起釋放連接,連接通道將會一直打開,這時如果有一方出現了掉電、死機、異常重啟等意外情況,連接另一方不知道發生異常將會一直保持連接造成資源浪費。為了解決這個問題,在傳輸層可以利用 TCP 的保活報文來實現。
在收到對端確認報文時設置保活定時器,時間通常小于兩小時, 每隔 75 秒發送一次。如果在保活時長內未收到對端數據,就會發送探測報文,如果連續發送 10 個探測報文仍無反應,則可認為對端出現故障,接著就關閉連接。
TCP Keepalive 雖不是標準規范,但操作系統一旦實現,默認情況下須為關閉,可以被上層應用開啟和關閉。
3.11 安全
可靠性
- 應答機制
- 重發機制
- 滑動窗口
- 擁塞控制
半連接隊列
當客戶端發送 SYN 到服務端,服務端收到以后回復 ACK 和 SYN,狀態由 LISTEN 變為 SYN_RCVD,此時這個連接就被推入了 SYN 隊列,也就是半連接隊列。
全連接隊列
當客戶端返回 ACK, 服務端接收后,三次握手完成。這個時候連接等待被具體的應用取走,在被取走之前,它會被推入另外一個 TCP 維護的隊列,也就是全連接隊列(Accept Queue)。
SYN Flood 攻擊
SYN Flood 是一種拒絕服務(DDoS)攻擊,其目的是通過消耗所有可用的服務器資源使服務器不可用于合法流量。通過重復發送初始連接請求(SYN)數據包,攻擊者能夠壓倒目標服務器機器上的所有可用端口,導致目標設備根本不響應合法流量。
攻擊者偽造大量的不存在的 IP 向服務器端發起連接請求,占滿服務器端的半連接隊列導致服務器無法響應后續的正常請求。
如何應對 SYN Flood 攻擊?
- 增加半連接隊列的容量,回收最早的半連接。
- 限制 SYN 并發數量、超時時間.
- 啟用 SYN cookie。
3.12 TCP 與 UDP 的區別
| 特性 | TCP | UDP |
| 可靠性 | 可靠的 | 不可靠 |
| 連接性 | 面向連接 | 無連接 |
| 報文 | 面向字節流 | 面向報文 |
| 效率 | 傳輸效率低 | 傳輸效率高 |
| 雙工性 | 全雙工 | 一對一、一對多、多對多 |
| 流量控制 | 滑動窗口| 無 |
| 擁塞控制 | 慢開始、擁塞避免、快重傳、快恢復 | 無 |
| 傳輸速度 | 慢 | 塊 |
| 應用場景 | 適用于要求可靠傳輸的應用,例如文件傳輸 | 適用于實時應用(IP 電話、視頻會議、直播等) |
Reference
- TCP 協議詳解之 TCP
- TCP 協議簡介
- 兩張動圖-徹底明白 TCP 的三次握手與四次揮手
- Java 技術干貨-知乎專欄
- TCP Fast Open
- 聊聊 TCP 中的 KeepAlive 機制
- 圖解 SSL 從回車到握手
- 你還在為 TCP 重傳、滑動窗口、流量控制、擁塞控制發愁嗎?看完圖解就不愁了
- TCP 流量控制、擁塞控制
- 一篇文章告訴你,TLS 1.3 如何用性能為 HTTPS 正名
4. IP 協議(網絡層)
IP 協議為上層協議提供無狀態、無連接、不可靠的服務。
4.1 特性
-
無狀態
- 無狀態是指 IP 通信雙方不同步傳輸數據的狀態信息,所有 IP 數據報的發送、傳輸、接受都是相互獨立、沒有上下文關系的。這種服務優點在于簡單、高效。最大的缺點是無法處理亂序和重復的 IP 數據報,確保 IP 數據報完整的工作只能交給上層協議來完成。
-
無連接
- 無連接是指 IP 通信雙方都不長久地維持對方的任何信息。上層協議每次發送數據的時候,都需要明確指出對方的 IP 地址。
-
不可靠
- 不可靠是指 IP 協議不能保證 IP 數據報準確到達接收端,它指承諾盡最大努力交付。IP 模塊一旦檢測到數據報發送失敗,就通知上層協議,而不會試圖重傳。
4.2 IP 報頭
Version(版本號):標識 IP 協議的版本,目前 V4 版本地址已經枯竭,V6 慢慢成為主流。
Header Length(頭部長度):默認為 20 字節,最大為 60 字節。
Differentiated Services Field (服務區分符):用于為不同的 IP 數據包定義不同的服務質量,一般應用在 QoS 技術中。
Total Length (總長度):標識 IP 頭部加上上層數據的數據包大小,IP 包總長度最大為 65535 個字節。
Identification (標識符):用來實現 IP 分片的重組,標識分片屬于哪個進程,不同進程通過不同 ID 區分。
Flags(標志符):用來確認是否還有 IP 分片或是否能執行分片。
Fragment offset (分片偏移量):用于標識 IP 分片的位置,實現 IP 分片的重組。
Time to live (生存時間):標識 IP 數據包還能生存多久,根據操作系統不同,TTL 默認值不同,每經過一個三層設備如路由器的處理,則 TTL 減去 1,當 TTL=0 時,則此數據包被丟棄。
Protocol (協議號):標識 IP 協議上層應用。當上層協議為 ICMP 時,協議號為 1,TCP 協議號為 6,UDP 的協議號為 17。
Header checksum (頭部校驗):用于檢驗 IP 數據包是否完整或被修改,若校驗失敗則丟棄數據包。
Source(源 IP 地址):標識發送者 IP 地址,占用 32bit。
Destination (目的 IP 地址):標識接收者 IP 地址,占用 32bit。
4.3 IP 地址類型
- 公有地址
- 公有地址(Public address)由 Inter NIC(Internet Network Information Center 因特網信息中心)負責。這些 IP 地址分配給注冊并向 Inter NIC 提出申請的組織機構。通過它直接訪問因特網。
- 私有地址
- 私有地址(Private address)屬于非注冊地址,專門為組織機構內部使用。以下列出留用的內部私有地址 A 類 10.0.0.0--10.255.255.255B 類 172.16.0.0--172.31.255.255C 類 192.168.0.0--192.168.255.255
4.4 IP 地址分類
IP 地址分類成了 5 種類型,分別是 A 類、B 類、C 類、D 類、E 類。