HTTP 的前世今生
HTTP狀態碼
- 1xx 信息性狀態碼 websocket upgrade
- 2xx 成功狀態碼
- 200 服務器已成功處理了請求
- 204(沒有響應體)
- 206(范圍請求 暫停繼續下載)
- 3xx 重定向狀態碼
- 301(永久) :請求的頁面已永久跳轉到新的url
- 302(臨時) :允許各種各樣的重定向,一般情況下都會實現為到 GET 的重定向,但是不能確保 POST 會重定向為 POST
- 303 只允許任意請求到 GET 的重定向
- 304 未修改:自從上次請求后,請求的網頁未修改過
- 307:307 和 302 一樣,除了不允許 POST 到 GET 的重定向
- 4xx 客戶端錯誤狀態碼
- 400 客戶端參數錯誤
- 401 沒有登錄
- 403 登錄了沒權限 比如管理系統
- 404 頁面不存在
- 405 禁用請求中指定的方法
- 5xx 服務端錯誤狀態碼
- 500 服務器錯誤:服務器內部錯誤,無法完成請求
- 502 錯誤網關:服務器作為網關或代理出現錯誤
- 503 服務不可用:服務器目前無法使用
- 504 網關超時:網關或代理服務器,未及時獲取請求
1. HTTP前生今世
- HTTP 協議始于三十年前蒂姆·伯納斯 - 李的一篇論文
- HTTP/0.9 是個簡單的文本協議,只能獲取文本資源;
- HTTP/1.0 確立了大部分現在使用的技術,但它不是正式標準;
- HTTP/1.1 是目前互聯網上使用最廣泛的協議,功能也非常完善;
- HTTP/2 基于 Google 的 SPDY 協議,注重性能改善,但還未普及;
- HTTP/3 基于 Google 的 QUIC 協議,是將來的發展方向
2. HTTP世界全覽
- 互聯網上絕大部分資源都使用 HTTP 協議傳輸;
- 瀏覽器是 HTTP 協議里的請求方,即 User Agent;
- 服務器是 HTTP 協議里的應答方,常用的有 Apache 和 Nginx;
- CDN 位于瀏覽器和服務器之間,主要起到緩存加速的作用;
- 爬蟲是另一類 User Agent,是自動訪問網絡資源的程序。
- TCP/IP 是網絡世界最常用的協議,HTTP 通常運行在 TCP/IP 提供的可靠傳輸基礎上
- DNS 域名是 IP 地址的等價替代,需要用域名解析實現到 IP 地址的映射;
- URI 是用來標記互聯網上資源的一個名字,由“協議名 + 主機名 + 路徑”構成,俗稱 URL;
- HTTPS 相當于“HTTP+SSL/TLS+TCP/IP”,為 HTTP 套了一個安全的外殼;
- 代理是 HTTP 傳輸過程中的“中轉站”,可以實現緩存加速、負載均衡等功能
3. HTTP分層
- 第一層:物理層,TCP/IP 里無對應;
- 第二層:數據鏈路層,對應 TCP/IP 的鏈接層;
- 第三層:網絡層,對應 TCP/IP 的網際層;
- 第四層:傳輸層,對應 TCP/IP 的傳輸層;
- 第五、六、七層:統一對應到 TCP/IP 的應用層
總結
- TCP/IP 分為四層,核心是二層的 IP 和三層的 TCP,HTTP 在第四層;
- OSI 分為七層,基本對應 TCP/IP,TCP 在第四層,HTTP 在第七層;
- OSI 可以映射到 TCP/IP,但這期間一、五、六層消失了;
- 日常交流的時候我們通常使用 OSI 模型,用四層、七層等術語;
- HTTP 利用 TCP/IP協議棧逐層打包再拆包,實現了數據傳輸,但下面的細節并不可見
有一個辨別四層和七層比較好的(但不是絕對的)小竅門,“兩個凡是”:凡是由操作系統負責處理的就是四層或四層以下,否則,凡是需要由應用程序(也就是你自己寫代碼)負責處理的就是七層
4. HTTP報文是什么樣子的
HTTP 協議的請求報文和響應報文的結構基本相同,由三大部分組成
- 起始行(start line):描述請求或響應的基本信息;
- 頭部字段集合(header):使用 key-value 形式更詳細地說明報文;
- 消息正文(entity):實際傳輸的數據,它不一定是純文本,可以是圖片、視頻等二進制數據
這其中前兩部分起始行和頭部字段經常又合稱為“請求頭”或“響應頭”,消息正文又稱為“實體”,但與“header”對應,很多時候就直接稱為“body”。
一個完整的 HTTP 報文就像是下圖的這個樣子,注意在 header 和 body 之間有一個“空行”
5. HTTP之URL
- URI 是用來唯一標記服務器上資源的一個字符串,通常也稱為 URL;
- URI 通常由 scheme、host:port、path 和 query 四個部分組成,有的可以省略;
- scheme 叫“方案名”或者“協議名”,表示資源應該使用哪種協議來訪問;
- “host:port”表示資源所在的主機名和端口號;
- path 標記資源所在的位置;
- query 表示對資源附加的額外要求;
- 在 URI 里對“@&/”等特殊字符和漢字必須要做編碼,否則服務器收到 HTTP報文后會無法正確處理
6. HTTP實體數據
1. 數據類型與編碼
- text:即文本格式的可讀數據,我們最熟悉的應該就是 text/html 了,表示超文本文檔,此外還有純文本 text/plain、樣式表 text/css 等。
- image:即圖像文件,有 image/gif、image/jpeg、image/png 等。
- audio/video:音頻和視頻數據,例如 audio/mpeg、video/mp4 等。
- application:數據格式不固定,可能是文本也可能是二進制,必須由上層應用程序來解釋。常見的有 application/json,application/javascript、application/pdf 等,另外,如果實在是不知道數據是什么類型,像剛才說的“黑盒”,就會是 application/octet-stream,即不透明的二進制數據
但僅有 MIME type 還不夠,因為 HTTP 在傳輸時為了節約帶寬,有時候還會壓縮數據,為了不要讓瀏覽器繼續“猜”,還需要有一個“Encoding type”,告訴數據是用的什么編碼格式,這樣對方才能正確解壓縮,還原出原始的數據。
比起 MIME type 來說,Encoding type 就少了很多,常用的只有下面三種
- gzip:GNU zip 壓縮格式,也是互聯網上最流行的壓縮格式;
- deflate:zlib(deflate)壓縮格式,流行程度僅次于 gzip;
- br:一種專門為 HTTP 優化的新壓縮算法(Brotli)
2. 數據類型使用的頭字段
有了 MIME type 和 Encoding type,無論是瀏覽器還是服務器就都可以輕松識別出 body 的類型,也就能夠正確處理數據了。
HTTP 協議為此定義了兩個 Accept 請求頭字段和兩個 Content 實體頭字段,用于客戶端和服務器進行“內容協商”。也就是說,客戶端用 Accept 頭告訴服務器希望接收什么樣的數據,而服務器用 Content 頭告訴客戶端實際發送了什么樣的數據
Accept字段標記的是客戶端可理解的 MIME type,可以用“,”做分隔符列出多個類型,讓服務器有更多的選擇余地,例如下面的這個頭:
Accept: text/html,application/xml,image/webp,image/png這就是告訴服務器:“我能夠看懂 HTML、XML 的文本,還有 webp 和 png 的圖片,請給我這四類格式的數據”。
相應的,服務器會在響應報文里用頭字段Content-Type告訴實體數據的真實類型:
Content-Type: text/html Content-Type: image/png這樣瀏覽器看到報文里的類型是“text/html”就知道是 HTML 文件,會調用排版引擎渲染出頁面,看到“image/png”就知道是一個 PNG 文件,就會在頁面上顯示出圖像。
Accept-Encoding字段標記的是客戶端支持的壓縮格式,例如上面說的 gzip、deflate 等,同樣也可以用“,”列出多個,服務器可以選擇其中一種來壓縮數據,實際使用的壓縮格式放在響應頭字段Content-Encoding里
Accept-Encoding: gzip, deflate, br Content-Encoding: gzip不過這兩個字段是可以省略的,如果請求報文里沒有 Accept-Encoding 字段,就表示客戶端不支持壓縮數據;如果響應報文里沒有 Content-Encoding 字段,就表示響應數據沒有被壓縮
3. 語言類型使用的頭字段
同樣的,HTTP 協議也使用 Accept 請求頭字段和 Content 實體頭字段,用于客戶端和服務器就語言與編碼進行“內容協商”。
Accept-Language字段標記了客戶端可理解的自然語言,也允許用“,”做分隔符列出多個類型,例如:
Accept-Language: zh-CN, zh, en這個請求頭會告訴服務器:“最好給我 zh-CN 的漢語文字,如果沒有就用其他的漢語方言,如果還沒有就給英文”。
相應的,服務器應該在響應報文里用頭字段Content-Language告訴客戶端實體數據使用的實際語言類型
Content-Language: zh-CN- 字符集在 HTTP 里使用的請求頭字段是Accept-Charset,但響應頭里卻沒有對應的 Content-Charset,而是在Content-Type字段的數據類型后面用“charset=xxx”來表示,這點需要特別注意。
- 例如,瀏覽器請求 GBK 或 UTF-8 的字符集,然后服務器返回的是 UTF-8 編碼,就是下面這樣
不過現在的瀏覽器都支持多種字符集,通常不會發送 Accept-Charset,而服務器也不會發送 Content-Language,因為使用的語言完全可以由字符集推斷出來,所以在請求頭里一般只會有 Accept-Language 字段,響應頭里只會有 Content-Type字段
4. 內容協商的質量值
在 HTTP 協議里用 Accept、Accept-Encoding、Accept-Language 等請求頭字段進行內容協商的時候,還可以用一種特殊的“q”參數表示權重來設定優先級,這里的“q”是“quality factor”的意思。
權重的最大值是 1,最小值是 0.01,默認值是 1,如果值是 0 就表示拒絕。具體的形式是在數據類型或語言代碼后面加一個“;”,然后是“q=value”。
這里要提醒的是“;”的用法,在大多數編程語言里“;”的斷句語氣要強于“,”,而在 HTTP 的內容協商里卻恰好反了過來,“;”的意義是小于“,”的。
例如下面的 Accept 字段:
Accept: text/html,application/xml;q=0.9,*/*;q=0.8它表示瀏覽器最希望使用的是 HTML 文件,權重是 1,其次是 XML 文件,權重是 0.9,最后是任意數據類型,權重是0.8。服務器收到請求頭后,就會計算權重,再根據自己的實際情況優先輸出 HTML 或者 XML
5. 內容協商的結果
內容協商的過程是不透明的,每個 Web 服務器使用的算法都不一樣。但有的時候,服務器會在響應頭里多加一個Vary字段,記錄服務器在內容協商時參考的請求頭字段,給出一點信息,例如:
Vary: Accept-Encoding,User-Agent,Accept這個 Vary 字段表示服務器依據了 Accept-Encoding、User-Agent 和 Accept 這三個頭字段,然后決定了發回的響應報文。
Vary 字段可以認為是響應報文的一個特殊的“版本標記”。每當 Accept 等請求頭變化時,Vary 也會隨著響應報文一起變化。也就是說,同一個 URI 可能會有多個不同的“版本”,主要用在傳輸鏈路中間的代理服務器實現緩存服務,這個之后講“HTTP 緩存”時還會再提到
6. 小結
- 數據類型表示實體數據的內容是什么,使用的是 MIME type,相關的頭字段是 Accept和 Content-Type;
- 數據編碼表示實體數據的壓縮方式,相關的頭字段是 Accept-Encoding 和 Content-Encoding;
- 語言類型表示實體數據的自然語言,相關的頭字段是 Accept-Language 和 Content-Language;
- 字符集表示實體數據的編碼方式,相關的頭字段是 Accept-Charset和 Content-Type;
- 客戶端需要在請求頭里使用 Accept 等頭字段與服務器進行“內容協商”,要求服務器返回最合適的數據; Accept 等頭字段可以用“,”順序列出多個可能的選項,還可以用“;q=”參數來精確指定權重
7. 談一談HTTP協議優缺點
超文本傳輸協議,HTTP 是一個在計算機世界里專門在兩點之間傳輸文字、圖片、音頻、視頻等超文本數據的約定和規范。
- HTTP 特點
- 靈活可擴展。一個是語法上只規定了基本格式,空格分隔單詞,換行分隔字段等。另外一個就是傳輸形式上不僅可以傳輸文本,還可以傳輸圖片,視頻等任意數據。
- 請求-應答模式,通常而言,就是一方發送消息,另外一方要接受消息,或者是做出相應等。
- 可靠傳輸,HTTP是基于TCP/IP,因此把這一特性繼承了下來。
- 無狀態,這個分場景回答即可。
- HTTP 缺點
- 無狀態,有時候,需要保存信息,比如像購物系統,需要保留下顧客信息等等,另外一方面,有時候,無狀態也會減少網絡開銷,比如類似直播行業這樣子等,這個還是分場景來說。
- 明文傳輸,即協議里的報文(主要指的是頭部)不使用二進制數據,而是文本形式。這讓HTTP的報文信息暴露給了外界,給攻擊者帶來了便利。
- 隊頭阻塞,當http開啟長連接時,共用一個TCP連接,當某個請求時間過長時,其他的請求只能處于阻塞狀態,這就是隊頭阻塞問題。
http 無狀態無連接
- http 協議對于事務處理沒有記憶能力
- 對同一個url請求沒有上下文關系
- 每次的請求都是獨立的,它的執行情況和結果與前面的請求和之后的請求是無直接關系的,它不會受前面的請求應答情況直接影響,也不會直接影響后面的請求應答情況
- 服務器中沒有保存客戶端的狀態,客戶端必須每次帶上自己的狀態去請求服務器
- 人生若只如初見,請求過的資源下一次會繼續進行請求
http協議無狀態中的 狀態 到底指的是什么?!
- 【狀態】的含義就是:客戶端和服務器在某次會話中產生的數據
- 那么對應的【無狀態】就意味著:這些數據不會被保留
- 通過增加cookie和session機制,現在的網絡請求其實是有狀態的
- 在沒有狀態的http協議下,服務器也一定會保留你每次網絡請求對數據的修改,但這跟保留每次訪問的數據是不一樣的,保留的只是會話產生的結果,而沒有保留會話
8. 說一說HTTP 的請求方法
- HTTP1.0定義了三種請求方法: GET, POST 和 HEAD方法
- HTTP1.1新增了五種請求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT
http/1.1規定了以下請求方法(注意,都是大寫):
- GET: 請求獲取Request-URI所標識的資源
- POST: 在Request-URI所標識的資源后附加新的數據
- HEAD: 請求獲取由Request-URI所標識的資源的響應消息報頭
- PUT: 請求服務器存儲一個資源,并用Request-URI作為其標識(修改數據)
- DELETE: 請求服務器刪除對應所標識的資源
- TRACE: 請求服務器回送收到的請求信息,主要用于測試或診斷
- CONNECT: 建立連接隧道,用于代理服務器
- OPTIONS: 列出可對資源實行的請求方法,用來跨域請求
從應用場景角度來看,Get 多用于無副作用,冪等的場景,例如搜索關鍵字。Post 多用于副作用,不冪等的場景,例如注冊。
options 方法有什么用
- OPTIONS 請求與 HEAD 類似,一般也是用于客戶端查看服務器的性能。
- 這個方法會請求服務器返回該資源所支持的所有 HTTP 請求方法,該方法會用’*'來代替資源名稱,向服務器發送 OPTIONS 請求,可以測試服務器功能是否正常。
- JS 的 XMLHttpRequest對象進行 CORS 跨域資源共享時,對于復雜請求,就是使用 OPTIONS 方法發送嗅探請求,以判斷是否有對指定資源的訪問權限。
9. 談一談GET 和 POST 的區別
本質上,只是語義上的區別,GET 用于獲取資源,POST 用于提交資源。
具體差別👇
- 從緩存角度看,GET 請求后瀏覽器會主動緩存,POST 默認情況下不能。
- 從參數角度來看,GET請求一般放在URL中,因此不安全,POST請求放在請求體中,相對而言較為安全,但是在抓包的情況下都是一樣的。
- 從編碼角度看,GET請求只能經行URL編碼,只能接受ASCII碼,而POST支持更多的編碼類型且不對數據類型限值。
- GET請求冪等,POST請求不冪等,冪等指發送 M 和 N 次請求(兩者不相同且都大于1),服務器上資源的狀態一致。
- GET請求會一次性發送請求報文,POST請求通常分為兩個TCP數據包,首先發 header 部分,如果服務器響應 100(continue), 然后發 body 部分。
10. 談一談隊頭阻塞問題
什么是隊頭阻塞?
對于每一個HTTP請求而言,這些任務是會被放入一個任務隊列中串行執行的,一旦隊首任務請求太慢時,就會阻塞后面的請求處理,這就是HTTP隊頭阻塞問題。
有什么解決辦法嗎👇
并發連接
我們知道對于一個域名而言,是允許分配多個長連接的,那么可以理解成增加了任務隊列,也就是說不會導致一個任務阻塞了該任務隊列的其他任務,在RFC規范中規定客戶端最多并發2個連接,不過實際情況就是要比這個還要多,舉個例子,Chrome中是6個。
域名分片
- 顧名思義,我們可以在一個域名下分出多個二級域名出來,而它們最終指向的還是同一個服務器,這樣子的話就可以并發處理的任務隊列更多,也更好的解決了隊頭阻塞的問題。
- 舉個例子,比如TianTian.com,可以分出很多二級域名,比如Day1.TianTian.com,Day2.TianTian.com,Day3.TianTian.com,這樣子就可以有效解決隊頭阻塞問題。
11. 談一談HTTP數據傳輸
大概遇到的情況就分為定長數據 與 不定長數據的處理吧。
定長數據
對于定長的數據包而言,發送端在發送數據的過程中,需要設置Content-Length,來指明發送數據的長度。
當然了如果采用了Gzip壓縮的話,Content-Length設置的就是壓縮后的傳輸長度。
我們還需要知道的是👇
- Content-Length如果存在并且有效的話,則必須和消息內容的傳輸長度完全一致,也就是說,如果過短就會截斷,過長的話,就會導致超時。
- 如果采用短鏈接的話,直接可以通過服務器關閉連接來確定消息的傳輸長度。
- 那么在HTTP/1.0之前的版本中,Content-Length字段可有可無,因為一旦服務器關閉連接,我們就可以獲取到傳輸數據的長度了。
- 在HTTP/1.1版本中,如果是Keep-alive的話,chunked優先級高于Content-Length,若是非Keep-alive,跟前面情況一樣,Content-Length可有可無。
那怎么來設置Content-Length
舉個例子來看看👇
const server = require('http').createServer(); server.on('request', (req, res) => {if(req.url === '/index') {// 設置數據類型res.setHeader('Content-Type', 'text/plain');res.setHeader('Content-Length', 10);res.write("你好,使用的是Content-Length設置傳輸數據形式");} })server.listen(3000, () => {console.log("成功啟動--TinaTian"); })不定長數據
現在采用最多的就是HTTP/1.1版本,來完成傳輸數據,在保存Keep-alive狀態下,當數據是不定長的時候,我們需要設置新的頭部字段👇
Transfer-Encoding: chunked通過chunked機制,可以完成對不定長數據的處理,當然了,你需要知道的是
- 如果頭部信息中有Transfer-Encoding,優先采用Transfer-Encoding里面的方法來找到對應的長度。
- 如果設置了Transfer-Encoding,那么Content-Length將被忽視。
- 使用長連接的話,會持續的推送動態內容。
那我們來模擬一下吧👇
const server = require('http').createServer(); server.on('request', (req, res) => {if(req.url === '/index') {// 設置數據類型res.setHeader('Content-Type', 'text/html; charset=utf8');res.setHeader('Content-Length', 10);res.setHeader('Transfer-Encoding', 'chunked');res.write("你好,使用的是Transfer-Encoding設置傳輸數據形式");setTimeout(() => {res.write("第一次傳輸數據給您<br/>");}, 1000);res.write("騷等一下");setTimeout(() => {res.write("第一次傳輸數據給您");res.end()}, 3000);} })server.listen(3000, () => {console.log("成功啟動--TinaTian"); })上面使用的是nodejs中http模塊,有興趣的小伙伴可以去試一試,以上就是HTTP對定長數據和不定長數據傳輸過程中的處理手段。
12. cookie 和 session
- session: 是一個抽象概念,開發者為了實現中斷和繼續等操作,將 user agent和 server 之間一對一的交互,抽象為“會話”,進而衍生出“會話狀態”,也就是 session 的概念
- cookie:它是一個世紀存在的東西,http 協議中定義在 header 中的字段,可以認為是 session 的一種后端無狀態實現
現在我們常說的 session,是為了繞開 cookie 的各種限制,通常借助 cookie本身和后端存儲實現的,一種更高級的會話狀態實現
session 的常見實現要借助cookie來發送 sessionID
13. 介紹一下HTTPS和HTTP區別
HTTPS 要比 HTTPS 多了 secure 安全性這個概念,實際上, HTTPS 并不是一個新的應用層協議,它其實就是 HTTP + TLS/SSL 協議組合而成,而安全性的保證正是 SSL/TLS 所做的工作。
SSL
安全套接層(Secure Sockets Layer)
TLS
(傳輸層安全,Transport Layer Security)
現在主流的版本是 TLS/1.2, 之前的 TLS1.0、TLS1.1 都被認為是不安全的,在不久的將來會被完全淘汰。
HTTPS 就是身披了一層 SSL 的 HTTP。
那么區別有哪些呢👇
- HTTP 是明文傳輸協議,HTTPS 協議是由 SSL+HTTP 協議構建的可進行加密傳輸、身份認證的網絡協議,比 HTTP 協議安全。
- HTTPS比HTTP更加安全,對搜索引擎更友好,利于SEO,谷歌、百度優先索引HTTPS網頁。
- HTTPS標準端口443,HTTP標準端口80。
- HTTPS需要用到SSL證書,而HTTP不用。
我覺得記住以下兩點HTTPS主要作用就行👇
HTTPS的缺點
- 證書費用以及更新維護。
- HTTPS 降低一定用戶訪問速度(實際上優化好就不是缺點了)。
- HTTPS 消耗 CPU 資源,需要增加大量機器。
14. HTTPS握手過程
- 第一步,客戶端給出協議版本號、一個客戶端生成的隨機數(Client random),以及客戶端支持的加密方法
- 第二步,服務端確認雙方使用的加密方法,并給出數字證書、以及一個服務器生成的隨機數
- 第三步,客戶端確認數字證書有效,然后生成一個新的隨機數(Premaster secret),并使用數字證書中的公鑰,加密這個隨機數,發給服務端
- 第四步,服務端使用自己的私鑰,獲取客戶端發來的隨機數(即Premaster secret)。
- 第五步,客戶端和服務端根據約定的加密方法,使用前面的三個隨機數,生成"對話密鑰"(session key),用來加密接下來的整個對話過程
總結
- 客戶端發起 HTTPS 請求,服務端返回證書,客戶端對證書進行驗證,驗證通過后本地生成用于構造對稱加密算法的隨機數
- 通過證書中的公鑰對隨機數進行加密傳輸到服務端(隨機對稱密鑰),服務端接收后通過私鑰解密得到隨機對稱密鑰,之后的數據交互通過對稱加密算法進行加解密。(既有對稱加密,也有非對稱加密)
15. 介紹一個HTTPS工作原理
我們可以把HTTPS理解成HTTPS = HTTP + SSL/TLS
TLS/SSL 的功能實現主要依賴于三類基本算法:散列函數 、對稱加密和非對稱加密,其利用非對稱加密實現身份認證和密鑰協商,對稱加密算法采用協商的密鑰對數據加密,基于散列函數驗證信息的完整性。
1. 對稱加密
加密和解密用同一個秘鑰的加密方式叫做對稱加密。Client客戶端和Server端共用一套密鑰,這樣子的加密過程似乎很讓人理解,但是隨之會產生一些問題。
問題一: WWW萬維網有許許多多的客戶端,不可能都用秘鑰A進行信息加密,這樣子很不合理,所以解決辦法就是使用一個客戶端使用一個密鑰進行加密。
問題二:既然不同的客戶端使用不同的密鑰,那么對稱加密的密鑰如何傳輸? 那么解決的辦法只能是一端生成一個秘鑰,然后通過HTTP傳輸給另一端,那么這樣子又會產生新的問題。
問題三: 這個傳輸密鑰的過程,又如何保證加密?如果被中間人攔截,密鑰也會被獲取, 那么你會說對密鑰再進行加密,那又怎么保存對密鑰加密的過程,是加密的過程?
到這里,我們似乎想明白了,使用對稱加密的方式,行不通,所以我們需要采用非對稱加密👇
2. 非對稱加密
通過上面的分析,對稱加密的方式行不通,那么我們來梳理一下非對稱加密。采用的算法是RSA,所以在一些文章中也會看見傳統RSA握手,基于現在TLS主流版本是1.2,所以接下來梳理的是TLS/1.2握手過程。
非對稱加密中,我們需要明確的點是👇
- 有一對秘鑰,公鑰和私鑰。
- 公鑰加密的內容,只有私鑰可以解開,私鑰加密的內容,所有的公鑰都可以解開,這里說的公鑰都可以解開,指的是一對秘鑰。
- 公鑰可以發送給所有的客戶端,私鑰只保存在服務器端。
3. 主要工作流程
梳理起來,可以把TLS 1.2 握手過程分為主要的五步👇
- 步驟一:Client發起一個HTTPS請求,連接443端口。這個過程可以理解成是請求公鑰的過程。
- 步驟二:Server端收到請求后,通過第三方機構私鑰加密,會把數字證書(也可以認為是公鑰證書)發送給Client。
- 步驟三:
- 瀏覽器安裝后會自動帶一些權威第三方機構公鑰,使用匹配的公鑰對數字簽名進行解密。
- 根據簽名生成的規則對網站信息進行本地簽名生成,然后兩者比對。
- 通過比對兩者簽名,匹配則說明認證通過,不匹配則獲取證書失敗。
- 步驟四:在安全拿到服務器公鑰后,客戶端Client隨機生成一個對稱密鑰,使用服務器公鑰(證書的公鑰)加密這個對稱密鑰,發送給Server(服務器)。
- 步驟五:Server(服務器)通過自己的私鑰,對信息解密,至此得到了對稱密鑰,此時兩者都擁有了相同的對稱密鑰。
接下來,就可以通過該對稱密鑰對傳輸的信息加密/解密啦,從上面圖舉個例子👇
- Client用戶使用該對稱密鑰加密’明文內容B’,發送給Server(服務器)
- Server使用該對稱密鑰進行解密消息,得到明文內容B。
接下來考慮一個問題,如果公鑰被中間人拿到纂改怎么辦呢?
客戶端可能拿到的公鑰是假的,解決辦法是什么呢?
3. 第三方認證
客戶端無法識別傳回公鑰是中間人的,還是服務器的,這是問題的根本,我們是不是可以通過某種規范可以讓客戶端和服務器都遵循某種約定呢?那就是通過第三方認證的方式
在HTTPS中,通過 證書 + 數字簽名來解決這個問題。
這里唯一不同的是,假設對網站信息加密的算法是MD5,通過MD5加密后,然后通過第三方機構的私鑰再次對其加密,生成數字簽名。
這樣子的話,數字證書包含有兩個特別重要的信息👉某網站公鑰+數字簽名
我們再次假設中間人截取到服務器的公鑰后,去替換成自己的公鑰,因為有數字簽名的存在,這樣子客戶端驗證發現數字簽名不匹配,這樣子就防止中間人替換公鑰的問題。
那么客戶端是如何去對比兩者數字簽名的呢?
- 瀏覽器會去安裝一些比較權威的第三方認證機構的公鑰,比如VeriSign、Symantec以及GlobalSign等等。
- 驗證數字簽名的時候,會直接從本地拿到相應的第三方的公鑰,對私鑰加密后的數字簽名進行解密得到真正的簽名。
- 然后客戶端利用簽名生成規則進行簽名生成,看兩個簽名是否匹配,如果匹配認證通過,不匹配則獲取證書失敗。
4. 數字簽名作用
數字簽名:將網站的信息,通過特定的算法加密,比如MD5,加密之后,再通過服務器的私鑰進行加密,形成加密后的數字簽名。
第三方認證機構是一個公開的平臺,中間人可以去獲取。
如果沒有數字簽名的話,這樣子可以就會有下面情況👇
從上面我們知道,如果只是對網站信息進行第三方機構私鑰加密的話,還是會受到欺騙。
因為沒有認證,所以中間人也向第三方認證機構進行申請,然后攔截后把所有的信息都替換成自己的,客戶端仍然可以解密,并且無法判斷這是服務器的還是中間人的,最后造成數據泄露。
5. 總結
- HTTPS就是使用SSL/TLS協議進行加密傳輸
- 大致流程:客戶端拿到服務器的公鑰(是正確的),然后客戶端隨機生成一個對稱加密的秘鑰,使用該公鑰加密,傳輸給服務端,服務端再通過解密拿到該對稱秘鑰,后續的所有信息都通過該對稱秘鑰進行加密解密,完成整個HTTPS的流程。
- 第三方認證,最重要的是數字簽名,避免了獲取的公鑰是中間人的。
16. SSL 連接斷開后如何恢復
一共有兩種方法來恢復斷開的 SSL 連接,一種是使用 session ID,一種是 session ticket。
通過session ID
使用 session ID 的方式,每一次的會話都有一個編號,當對話中斷后,下一次重新連接時,只要客戶端給出這個編號,服務器如果有這個編號的記錄,那么雙方就可以繼續使用以前的秘鑰,而不用重新生成一把。目前所有的瀏覽器都支持這一種方法。但是這種方法有一個缺點是,session ID 只能夠存在一臺服務器上,如果我們的請求通過負載平衡被轉移到了其他的服務器上,那么就無法恢復對話。
通過session ticket
另一種方式是 session ticket 的方式,session ticket 是服務器在上一次對話中發送給客戶的,這個 ticket 是加密的,只有服務器能夠解密,里面包含了本次會話的信息,比如對話秘鑰和加密方法等。這樣不管我們的請求是否轉移到其他的服務器上,當服務器將 ticket 解密以后,就能夠獲取上次對話的信息,就不用重新生成對話秘鑰了。
17. 談一談你對HTTP/2理解
首先補充一下,http 和 https 的區別,相比于 http,https 是基于 ssl 加密的 http 協議
簡要概括:http2.0 是基于 1999 年發布的 http1.0 之后的首次更新
- 提升訪問速度(可以對于,請求資源所需時間更少,訪問速度更快,相比 http1.0)
- 允許多路復用:多路復用允許同時通過單一的 HTTP/2 連接發送多重請求-響應信息。改 善了:在 http1.1 中,瀏覽器客戶端在同一時間,針對同一域名下的請求有一定數量限 制(連接數量),超過限制會被阻塞
- 二進制分幀:HTTP2.0 會將所有的傳輸信息分割為更小的信息或者幀,并對他們進行二 進制編碼
- 首部壓縮
- 服務器端推送
頭部壓縮
HTTP 1.1版本會出現 User-Agent、Cookie、Accept、Server、Range 等字段可能會占用幾百甚至幾千字節,而 Body 卻經常只有幾十字節,所以導致頭部偏重。
HTTP 2.0 使用 HPACK 算法進行壓縮。
多路復用
- HTTP 1.x 中,如果想并發多個請求,必須使用多個 TCP 鏈接,且瀏覽器為了控制資源,還會對單個域名有 6-8個的TCP鏈接請求限制。
HTTP2中:
- 同域名下所有通信都在單個連接上完成。
- 單個連接可以承載任意數量的雙向數據流。
- 數據流以消息的形式發送,而消息又由一個或多個幀組成,多個幀之間可以亂序發送,因為根據幀首部的流標識可以重新組裝,也就是Stream ID,流標識符,有了它,接收方就能從亂序的二進制幀中選擇ID相同的幀,按照順序組裝成請求/響應報文。
服務器推送
瀏覽器發送一個請求,服務器主動向瀏覽器推送與這個請求相關的資源,這樣瀏覽器就不用發起后續請求。
相比較http/1.1的優勢👇
- 推送資源可以由不同頁面共享
- 服務器可以按照優先級推送資源
- 客戶端可以緩存推送的資源
- 客戶端可以拒收推送過來的資源
二進制分幀
之前是明文傳輸,不方便計算機解析,對于回車換行符來說到底是內容還是分隔符,都需要內部狀態機去識別,這樣子效率低,HTTP/2采用二進制格式,全部傳輸01串,便于機器解碼。
這樣子一個報文格式就被拆分為一個個二進制幀,用Headers幀存放頭部字段,Data幀存放請求體數據。這樣子的話,就是一堆亂序的二進制幀,它們不存在先后關系,因此不需要排隊等待,解決了HTTP隊頭阻塞問題。
在客戶端與服務器之間,雙方都可以互相發送二進制幀,這樣子雙向傳輸的序列,稱為流,所以HTTP/2中以流來表示一個TCP連接上進行多個數據幀的通信,這就是多路復用概念。
那亂序的二進制幀,是如何組裝成對于的報文呢?
- 所謂的亂序,值的是不同ID的Stream是亂序的,對于同一個Stream ID的幀是按順序傳輸的。
- 接收方收到二進制幀后,將相同的Stream ID組裝成完整的請求報文和響應報文。
- 二進制幀中有一些字段,控制著優先級和流量控制等功能,這樣子的話,就可以設置數據幀的優先級,讓服務器處理重要資源,優化用戶體驗。
HTTP2的缺點
- TCP 以及 TCP+TLS建立連接的延時,HTTP/2使用TCP協議來傳輸的,而如果使用HTTPS的話,還需要使用TLS協議進行安全傳輸,而使用TLS也需要一個握手過程,在傳輸數據之前,導致我們需要花掉 3~4 個 RTT。
- TCP的隊頭阻塞并沒有徹底解決。在HTTP/2中,多個請求是跑在一個TCP管道中的。但當HTTP/2出現丟包時,整個 TCP 都要開始等待重傳,那么就會阻塞該TCP連接中的所有請求。
18. HTTP3
Google 在推SPDY的時候就已經意識到了這些問題,于是就另起爐灶搞了一個基于 UDP 協議的“QUIC”協議,讓HTTP跑在QUIC上而不是TCP上。主要特性如下:
- 實現了類似TCP的流量控制、傳輸可靠性的功能。雖然UDP不提供可靠性的傳輸,但QUIC在UDP的基礎之上增加了一層來保證數據可靠性傳輸。它提供了數據包重傳、擁塞控制以及其他一些TCP中存在的特性
- 實現了快速握手功能。由于QUIC是基于UDP的,所以QUIC可以實現使用0-RTT或者1-RTT來建立連接,這意味著QUIC可以用最快的速度來發送和接收數據。
- 集成了TLS加密功能。目前QUIC使用的是TLS1.3,相較于早期版本TLS1.3有更多的優點,其中最重要的一點是減少了握手所花費的RTT個數。
- 多路復用,徹底解決TCP中隊頭阻塞的問題。
19. HTTP/1.0 HTTP1.1 HTTP2.0版本之間的差異
- HTTP 0.9:1991年,原型版本,功能簡陋,只有一個命令GET,只支持純文本內容,該版本已過時。
- HTTP 1.0
- 任何格式的內容都可以發送,這使得互聯網不僅可以傳輸文字,還能傳輸圖像、視頻、二進制等文件。
- 除了GET命令,還引入了POST命令和HEAD命令。
- http請求和回應的格式改變,除了數據部分,每次通信都必須包括頭信息(HTTP header),用來描述一些元數據。
- 只使用 header 中的 If-Modified-Since 和 Expires 作為緩存失效的標準。
- 不支持斷點續傳,也就是說,每次都會傳送全部的頁面和數據。
- 通常每臺計算機只能綁定一個 IP,所以請求消息中的 URL 并沒有傳遞主機名(hostname)
- HTTP 1.1 http1.1是目前最為主流的http協議版本,從1999年發布至今,仍是主流的http協議版本。
- 引入了持久連接( persistent connection),即TCP連接默認不關閉,可以被多個請求復用,不用聲明Connection: keep-alive。長連接的連接時長可以通過請求頭中的 keep-alive 來設置
- 引入了管道機制( pipelining),即在同一個TCP連接里,客戶端可以同時發送多個 請求,進一步改進了HTTP協議的效率。
- HTTP 1.1 中新增加了 E-tag,If-Unmodified-Since, If-Match, If-None-Match 等緩存控制標頭來控制緩存失效。
- 支持斷點續傳,通過使用請求頭中的 Range 來實現。
- 使用了虛擬網絡,在一臺物理服務器上可以存在多個虛擬主機(Multi-homed Web Servers),并且它們共享一個IP地址。
- 新增方法:PUT、 PATCH、 OPTIONS、 DELETE。
- http1.x版本問題
- 在傳輸數據過程中,所有內容都是明文,客戶端和服務器端都無法驗證對方的身份,無法保證數據的安全性。
- HTTP/1.1 版本默認允許復用TCP連接,但是在同一個TCP連接里,所有數據通信是按次序進行的,服務器通常在處理完一個回應后,才會繼續去處理下一個,這樣子就會造成隊頭阻塞。
- http/1.x 版本支持Keep-alive,用此方案來彌補創建多次連接產生的延遲,但是同樣會給服務器帶來壓力,并且的話,對于單文件被不斷請求的服務,Keep-alive會極大影響性能,因為它在文件被請求之后還保持了不必要的連接很長時間。
- HTTP 2.0
- 二進制分幀 這是一次徹底的二進制協議,頭信息和數據體都是二進制,并且統稱為"幀":頭信息幀和數據幀。
- 頭部壓縮 HTTP 1.1版本會出現 User-Agent、Cookie、Accept、Server、Range 等字段可能會占用幾百甚至幾千字節,而 Body 卻經常只有幾十字節,所以導致頭部偏重。HTTP 2.0 使用 HPACK 算法進行壓縮。
- 多路復用 復用TCP連接,在一個連接里,客戶端和瀏覽器都可以同時發送多個請求或回應,且不用按順序一一對應,這樣子解決了隊頭阻塞的問題。
- 服務器推送 允許服務器未經請求,主動向客戶端發送資源,即服務器推送。
- 請求優先級 可以設置數據幀的優先級,讓服務端先處理重要資源,優化用戶體驗。
20. DNS如何工作的
DNS 的作用就是通過域名查詢到具體的 IP。DNS 協議提供的是一種主機名到 IP 地址的轉換服務,就是我們常說的域名系統。是應用層協議,通常該協議運行在UDP協議之上,使用的是53端口號。
因為 IP 存在數字和英文的組合(IPv6),很不利于人類記憶,所以就出現了域名。你可以把域名看成是某個 IP 的別名,DNS 就是去查詢這個別名的真正名稱是什么。
當你在瀏覽器中想訪問 www.google.com 時,會通過進行以下操作:
- 本地客戶端向服務器發起請求查詢 IP 地址
- 查看瀏覽器有沒有該域名的 IP 緩存
- 查看操作系統有沒有該域名的 IP 緩存
- 查看 Host 文件有沒有該域名的解析配置
- 如果這時候還沒得話,會通過直接去 DNS 根服務器查詢,這一步查詢會找出負責 com 這個一級域名的服務器
- 然后去該服務器查詢 google.com 這個二級域名
- 接下來查詢 www.google.com 這個三級域名的地址
- 返回給 DNS 客戶端并緩存起來
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-De8BCewP-1658290547545)(https://s.poetries.work/images/20210503210054.png)]
我們通過一張圖來看看它的查詢過程吧👇
這張圖很生動的展示了DNS在本地DNS服務器是如何查詢的,一般向本地DNS服務器發送請求是遞歸查詢的
本地 DNS 服務器向其他域名服務器請求的過程是迭代查詢的過程👇
遞歸查詢和迭代查詢
- 遞歸查詢指的是查詢請求發出后,域名服務器代為向下一級域名服務器發出請求,最后向用戶返回查詢的最終結果。使用遞歸 查詢,用戶只需要發出一次查詢請求。
- 迭代查詢指的是查詢請求后,域名服務器返回單次查詢的結果。下一級的查詢由用戶自己請求。使用迭代查詢,用戶需要發出 多次的查詢請求。
所以一般而言,本地服務器查詢是遞歸查詢,而本地 DNS 服務器向其他域名服務器請求的過程是迭代查詢的過程
DNS緩存
緩存也很好理解,在一個請求中,當某個DNS服務器收到一個DNS回答后,它能夠回答中的信息緩存在本地存儲器中。返回的資源記錄中的 TTL 代表了該條記錄的緩存的時間。
DNS實現負載平衡
它是如何實現負載均衡的呢?首先我們得清楚DNS 是可以用于在冗余的服務器上實現負載平衡。
原因: 這是因為一般的大型網站使用多臺服務器提供服務,因此一個域名可能會對應 多個服務器地址。
舉個例子來說👇
- 當用戶發起網站域名的 DNS 請求的時候,DNS 服務器返回這個域名所對應的服務器 IP 地址的集合
- 在每個回答中,會循環這些 IP 地址的順序,用戶一般會選擇排在前面的地址發送請求。
- 以此將用戶的請求均衡的分配到各個不同的服務器上,這樣來實現負載均衡。
DNS 為什么使用 UDP 協議作為傳輸層協議?
DNS 使用 UDP 協議作為傳輸層協議的主要原因是為了避免使用 TCP 協議時造成的連接時延
- 為了得到一個域名的 IP 地址,往往會向多個域名服務器查詢,如果使用 TCP 協議,那么每次請求都會存在連接時延,這樣使 DNS 服務變得很慢。
- 大多數的地址查詢請求,都是瀏覽器請求頁面時發出的,這樣會造成網頁的等待時間過長。
總結
- DNS域名系統,是應用層協議,運行UDP協議之上,使用端口43。
- 查詢過程,本地查詢是遞歸查詢,依次通過瀏覽器緩存 —>> 本地hosts文件 —>> 本地DNS解析器 —>>本地DNS服務器 —>> 其他域名服務器請求。 接下來的過程就是迭代過程。
- 遞歸查詢一般而言,發送一次請求就夠,迭代過程需要用戶發送多次請求。
21. 短輪詢、長輪詢和 WebSocket 間的區別
1. 短輪詢
短輪詢的基本思路:
- 瀏覽器每隔一段時間向瀏覽器發送 http 請求,服務器端在收到請求后,不論是否有數據更新,都直接進行 響應。
- 這種方式實現的即時通信,本質上還是瀏覽器發送請求,服務器接受請求的一個過程,通過讓客戶端不斷的進行請求,使得客戶端能夠模擬實時地收到服務器端的數據的變化。
優缺點👇
- 優點是比較簡單,易于理解。
- 缺點是這種方式由于需要不斷的建立 http 連接,嚴重浪費了服務器端和客戶端的資源。當用戶增加時,服務器端的壓力就會變大,這是很不合理的。
2. 長輪詢
長輪詢的基本思路:
- 首先由客戶端向服務器發起請求,當服務器收到客戶端發來的請求后,服務器端不會直接進行響應,而是先將 這個請求掛起,然后判斷服務器端數據是否有更新。
- 如果有更新,則進行響應,如果一直沒有數據,則到達一定的時間限制才返回。客戶端 JavaScript 響應處理函數會在處理完服務器返回的信息后,再次發出請求,重新建立連接。
優缺點👇
- 長輪詢和短輪詢比起來,它的優點是明顯減少了很多不必要的 http 請求次數,相比之下節約了資源。
- 長輪詢的缺點在于,連接掛起也會導致資源的浪費
3. WebSocket
- WebSocket 是 Html5 定義的一個新協議,與傳統的 http 協議不同,該協議允許由服務器主動的向客戶端推送信息。
- 使用 WebSocket 協議的缺點是在服務器端的配置比較復雜。WebSocket 是一個全雙工的協議,也就是通信雙方是平等的,可以相互發送消息。
22. 說一說正向代理和反向代理
正向代理
我們常說的代理也就是指正向代理,正向代理的過程,它隱藏了真實的請求客戶端,服務端不知道真實的客戶端是誰,客戶端請求的服務都被代理服務器代替來請求。
反向代理
這種代理模式下,它隱藏了真實的服務端,當我們向一個網站發起請求的時候,背后可能有成千上萬臺服務器為我們服務,具體是哪一臺,我們不清楚,我們只需要知道反向代理服務器是誰就行,而且反向代理服務器會幫我們把請求轉發到真實的服務器那里去,一般而言反向代理服務器一般用來實現負載平衡。
負載平衡的兩種實現方式?
- 一種是使用反向代理的方式,用戶的請求都發送到反向代理服務上,然后由反向代理服務器來轉發請求到真實的服務器上,以此來實現集群的負載平衡。
- 另一種是 DNS 的方式,DNS 可以用于在冗余的服務器上實現負載平衡。因為現在一般的大型網站使用多臺服務器提供服務,因此一個域名可能會對應多個服務器地址。當用戶向網站域名請求的時候,DNS 服務器返回這個域名所對應的服務器 IP 地址的集合,但在每個回答中,會循環這些 IP 地址的順序,用戶一般會選擇排在前面的地址發送請求。以此將用戶的請求均衡的分配到各個不同的服務器上,這樣來實現負載均衡。這種方式有一個缺點就是,由于 DNS 服務器中存在緩存,所以有可能一個服務器出現故障后,域名解析仍然返回的是那個 IP 地址,就會造成訪問的問題。
23. 介紹一下Connection:keep-alive
什么是keep-alive
我們知道HTTP協議采用“請求-應答”模式,當使用普通模式,即非KeepAlive模式時,每個請求/應答客戶和服務器都要新建一個連接,完成 之后立即斷開連接(HTTP協議為無連接的協議);
當使用Keep-Alive模式(又稱持久連接、連接重用)時,Keep-Alive功能使客戶端到服 務器端的連接持續有效,當出現對服務器的后繼請求時,Keep-Alive功能避免了建立或者重新建立連接。
為什么要使用keep-alive
keep-alive技術的創建目的,能在多次HTTP之前重用同一個TCP連接,從而減少創建/關閉多個 TCP 連接的開銷(包括響應時間、CPU 資源、減少擁堵等),參考如下示意圖
客戶端如何開啟
在HTTP/1.0協議中,默認是關閉的,需要在http頭加入"Connection: Keep-Alive”,才能啟用Keep-Alive;
Connection: keep-alivehttp 1.1中默認啟用Keep-Alive,如果加入"Connection: close “,才關閉。
Connection: close目前大部分瀏覽器都是用http1.1協議,也就是說默認都會發起Keep-Alive的連接請求了,所以是否能完成一個完整的Keep- Alive連接就看服務器設置情況。
24. http/https 協議總結
1.0 協議缺陷:
- 無法復用鏈接,完成即斷開,重新慢啟動和 TCP 3次握手
- head of line blocking: 線頭阻塞,導致請求之間互相影響
1.1 改進:
- 長連接(默認 keep-alive),復用
- host 字段指定對應的虛擬站點
- 新增功能:
- 斷點續傳
- 身份認證
- 狀態管理
- cache 緩存
- Cache-Control
- Expires
- Last-Modified
- Etag
2.0:
- 多路復用
- 二進制分幀層: 應用層和傳輸層之間
- 首部壓縮
- 服務端推送
https: 較為安全的網絡傳輸協議
- 證書(公鑰)
- SSL 加密
- 端口 443
TCP:
- 三次握手
- 四次揮手
- 滑動窗口: 流量控制
- 擁塞處理
- 慢開始
- 擁塞避免
- 快速重傳
- 快速恢復
緩存策略: 可分為 強緩存 和 協商緩存
- Cache-Control/Expires: 瀏覽器判斷緩存是否過期,未過期時,直接使用強緩存,Cache-Control的 max-age 優先級高于 Expires
- 當緩存已經過期時,使用協商緩存
- 唯一標識方案: Etag(response 攜帶) & If-None-Match(request攜帶,上一次返回的 Etag): 服務器判斷資源是否被修改
- 最后一次修改時間: Last-Modified(response) & If-Modified-Since(request,上一次返回的Last-Modified)
- 如果一致,則直接返回 304 通知瀏覽器使用緩存
- 如不一致,則服務端返回新的資源
- Last-Modified 缺點:
- 周期性修改,但內容未變時,會導致緩存失效
- 最小粒度只到 s, s 以內的改動無法檢測到
- Etag 的優先級高于Last-Modified
25. TCP為什么要三次握手
客戶端和服務端都需要直到各自可收發,因此需要三次握手
- 第一次握手成功讓服務端知道了客戶端具有發送能力
- 第二次握手成功讓客戶端知道了服務端具有接收和發送能力,但此時服務端并不知道客戶端是否接收到了自己發送的消息
- 所以第三次握手就起到了這個作用。經過三次通信后,服務端
你可以能會問,2 次握手就足夠了?。但其實不是,因為服務端還沒有確定客戶端是否準備好了。比如步驟 3 之后,服務端馬上給客戶端發送數據,這個時候客戶端可能還沒有準備好接收數據。因此還需要增加一個過程
TCP有6種標示:SYN(建立聯機) ACK(確認) PSH(傳送) FIN(結束) RST(重置) URG(緊急)
舉例:已失效的連接請求報文段
- client發送了第一個連接的請求報文,但是由于網絡不好,這個請求沒有立即到達服務端,而是在某個網絡節點中滯留了,直到某個時間才到達server
- 本來這已經是一個失效的報文,但是server端接收到這個請求報文后,還是會想client發出確認的報文,表示同意連接。
- 假如不采用三次握手,那么只要server發出確認,新的建立就連接了,但其實這個請求是失效的請求,client是不會理睬server的確認信息,也不會向服務端發送確認的請求
- 但是server認為新的連接已經建立起來了,并一直等待client發來數據,這樣,server的很多資源就沒白白浪費掉了
- 采用三次握手就是為了防止這種情況的發生,server會因為收不到確認的報文,就知道client并沒有建立連接。這就是三次握手的作用
三次握手過程中可以攜帶數據嗎
- 第一次、第二次握手不可以攜帶數據,因為一握二握時還沒有建立連接,會讓服務器容易受到攻擊
- 而第三次握手,此時客戶端已經處于 ESTABLISHED (已建立連接狀態) ,對于客戶端來說,已經建立起連接了,并且也已經知道服務器的接收、發送能力是正常的了,所以能攜帶數據也是沒問題的。
為什么建立連接只通信了三次,而斷開連接卻用了四次?
- 客戶端要求斷開連接,發送一個斷開的請求,這個叫作(FIN)。
- 服務端收到請求,然后給客戶端一個 ACK,作為 FIN 的響應。
- 這里你需要思考一個問題,可不可以像握手那樣馬上傳 FIN 回去?
- 其實這個時候服務端不能馬上傳 FIN,因為斷開連接要處理的問題比較多,比如說服務端可能還有發送出去的消息沒有得到 ACK;也有可能服務端自己有資源要釋放。因此斷開連接不能像握手那樣操作——將兩條消息合并。所以,服務端經過一個等待,確定可以關閉連接了,再發一條 FIN 給客戶端。
- 客戶端收到服務端的 FIN,同時客戶端也可能有自己的事情需要處理完,比如客戶端有發送給服務端沒有收到 ACK 的請求,客戶端自己處理完成后,再給服務端發送一個 ACK。
為了確保數據能夠完成傳輸。因為當服務端收到客戶端的 FIN 報文后,發送的 ACK 報文只是用來應答的,并不表示服務端也希望立即關閉連接。
當只有服務端把所有的報文都發送完了,才會發送 FIN 報文,告訴客戶端可以斷開連接了,因此在斷開連接時需要四次揮手。
- 關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你所有的數據都全部發送給對方了
- 所以你未必會馬上關閉SOCKET,也即你可能還需要發送一些數據給對方之后,再發送FIN報文給對方來表示你同意現在可以關閉連接了,所以它這里的ACK報文和FIN報文多數情況下都是分開發送的。
26. 為什么要有 WebSocket
已經有了被廣泛應用的 HTTP 協議,為什么要再出一個 WebSocket 呢?它有哪些好處呢?
其實 WebSocket 與 HTTP/2 一樣,都是為了解決 HTTP 某方面的缺陷而誕生的。HTTP/2 針對的是“隊頭阻塞”,而 WebSocket 針對的是“請求 - 應答”通信模式。
那么,“請求 - 應答”有什么不好的地方呢?
- “請求 - 應答”是一種“半雙工”的通信模式,雖然可以雙向收發數據,但同一時刻只能一個方向上有動作,傳輸效率低。更關鍵的一點,它是一種“被動”通信模式,服務器只能“被動”響應客戶端的請求,無法主動向客戶端發送數據。
- 雖然后來的 HTTP/2、HTTP/3 新增了 Stream、Server Push 等特性,但“請求 - 應答”依然是主要的工作方式。這就導致 HTTP 難以應用在動態頁面、即時消息、網絡游戲等要求“實時通信”的領域。
- 在 WebSocket 出現之前,在瀏覽器環境里用 JavaScript 開發實時 Web 應用很麻煩。因為瀏覽器是一個“受限的沙盒”,不能用 TCP,只有 HTTP 協議可用,所以就出現了很多“變通”的技術,“輪詢”(polling)就是比較常用的的一種。
- 簡單地說,輪詢就是不停地向服務器發送 HTTP 請求,問有沒有數據,有數據的話服務器就用響應報文回應。如果輪詢的頻率比較高,那么就可以近似地實現“實時通信”的效果。
- 但輪詢的缺點也很明顯,反復發送無效查詢請求耗費了大量的帶寬和 CPU 資源,非常不經濟。
- 所以,為了克服 HTTP“請求 - 應答”模式的缺點,WebSocket 就“應運而生”了
WebSocket 的特點
- WebSocket 是一個真正“全雙工”的通信協議,與 TCP 一樣,客戶端和服務器都可以隨時向對方發送數據
- WebSocket 采用了二進制幀結構,語法、語義與 HTTP 完全不兼容,但因為它的主要運行環境是瀏覽器,為了便于推廣和應用,就不得不“搭便車”,在使用習慣上盡量向 HTTP 靠攏,這就是它名字里“Web”的含義。
- 服務發現方面,WebSocket 沒有使用 TCP 的“IP 地址 + 端口號”,而是延用了 HTTP 的 URI 格式,但開頭的協議名不是“http”,引入的是兩個新的名字:“ws”和“wss”,分別表示明文和加密的 WebSocket 協議。
- WebSocket 的默認端口也選擇了 80 和 443,因為現在互聯網上的防火墻屏蔽了絕大多數的端口,只對 HTTP 的 80、443 端口“放行”,所以 WebSocket 就可以“偽裝”成 HTTP 協議,比較容易地“穿透”防火墻,與服務器建立連接
WebSocket 的握手
和 TCP、TLS 一樣,WebSocket 也要有一個握手過程,然后才能正式收發數據。
這里它還是搭上了 HTTP 的“便車”,利用了 HTTP 本身的“協議升級”特性,“偽裝”成 HTTP,這樣就能繞過瀏覽器沙盒、網絡防火墻等等限制,這也是 WebSocket 與 HTTP 的另一個重要關聯點。
WebSocket 的握手是一個標準的 HTTP GET 請求,但要帶上兩個協議升級的專用頭字段:
- “Connection: Upgrade”,表示要求協議“升級”;
- “Upgrade: websocket”,表示要“升級”成 WebSocket 協議。
另外,為了防止普通的 HTTP 消息被“意外”識別成 WebSocket,握手消息還增加了兩個額外的認證用頭字段(所謂的“挑戰”,Challenge):
- Sec-WebSocket-Key:一個 Base64 編碼的 16 字節隨機數,作為簡單的認證密鑰;
- Sec-WebSocket-Version:協議的版本號,當前必須是 13。
服務器收到 HTTP 請求報文,看到上面的四個字段,就知道這不是一個普通的 GET 請求,而是 WebSocket 的升級請求,于是就不走普通的 HTTP 處理流程,而是構造一個特殊的“101 Switching Protocols”響應報文,通知客戶端,接下來就不用 HTTP 了,全改用 WebSocket 協議通信
小結
瀏覽器是一個“沙盒”環境,有很多的限制,不允許建立 TCP 連接收發數據,而有了 WebSocket,我們就可以在瀏覽器里與服務器直接建立“TCP 連接”,獲得更多的自由。
不過自由也是有代價的,WebSocket 雖然是在應用層,但使用方式卻與“TCP Socket”差不多,過于“原始”,用戶必須自己管理連接、緩存、狀態,開發上比 HTTP 復雜的多,所以是否要在項目中引入 WebSocket 必須慎重考慮。
- HTTP 的“請求 - 應答”模式不適合開發“實時通信”應用,效率低,難以實現動態頁面,所以出現了 WebSocket;
- WebSocket 是一個“全雙工”的通信協議,相當于對 TCP 做了一層“薄薄的包裝”,讓它運行在瀏覽器環境里;
- WebSocket 使用兼容 HTTP 的 URI 來發現服務,但定義了新的協議名“ws”和“wss”,端口號也沿用了 80 和 443;
- WebSocket 使用二進制幀,結構比較簡單,特殊的地方是有個“掩碼”操作,客戶端發數據必須掩碼,服務器則不用;
- WebSocket 利用 HTTP 協議實現連接握手,發送 GET 請求要求“協議升級”,握手過程中有個非常簡單的認證機制,目的是防止誤連接。
27. UDP和TCP有什么區別
- TCP協議在傳送數據段的時候要給段標號;UDP協議不
- TCP協議可靠;UDP協議不可靠
- TCP協議是面向連接;UDP協議采用無連接
- TCP協議負載較高,采用虛電路;UDP采用無連接
- TCP協議的發送方要確認接收方是否收到數據段(3次握手協議)
- TCP協議采用窗口技術和流控制
總結
以上是生活随笔為你收集整理的HTTP 的前世今生的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2019年的咖啡大战,从杭州开打?
- 下一篇: 基于JAVA高铁在线购票系统计算机毕业设