1.1、如何通過三次握手建立連接
首先我們要清楚一個概念:http請求與TCP鏈接之間的關系,在客戶端向服務端請求和返回的過程中,是需要去創(chuàng)建一個TCP connection, 因為http是不存在鏈接這樣的概念的,它只有請求和響應的概念,請求和響應都是一個數(shù)據(jù)包,中間要通過一個傳輸通道,這個傳輸通道就是在TCP里面創(chuàng)建了一個從客戶端發(fā)起和服務端接收的一個鏈接,TCP 鏈接在創(chuàng)建的時候是有一個三次握手(三次網(wǎng)絡傳輸)這樣一個消耗在的。
TCP的核心思想:為了保證傳輸?shù)目煽?/strong>。 其次,保證傳輸效率。
為什么是三次握手: 第一次握手CLIENT告訴SERVER“我將要開始傳輸數(shù)據(jù)了”。 第二次握手SERVER告訴CLIENT“我已經(jīng)知道你將要傳輸數(shù)據(jù)了,我已經(jīng)做好準備”。 第三次握手CLIENT告訴SERVER“我已經(jīng)知道你已經(jīng)知道'我知道你已經(jīng)做好準備'”,SERVER端收到這個信號,開始傳輸數(shù)據(jù)。

TCP標示: SYN(synchronous
同步建立聯(lián)機)、 ACK(acknowledgement 確認)、 Sequence number(順序號碼)
第一次握手: 建立連接,客戶端A發(fā)送SYN = 1,隨機產(chǎn)生Seq=client_isn的數(shù)據(jù)包到服務器B,等待服務器確認。
第二次握手:服務器B收到請求后確認聯(lián)機(可以接收數(shù)據(jù)), 發(fā)起第二次握手請求, ACK=(A的Seq+1)、SYN=1, 隨機產(chǎn)生Seq=client_isn的數(shù)據(jù)包到A。
第三次握手:A收到后檢查ACK是否正確。若正確,A會在發(fā)送確認包ACK=服務器B的Seq+1、ACK=1, 服務器B收到后確認Seq 值與 ACK值,若正確,則建立連接。
TCP的狀態(tài) (SYN, FIN, ACK, PSH, RST, URG) eg
狀態(tài) 標志狀態(tài) 備注 SYN 表示建立連接 只是單個的一個SYN,它表示的只是建立連接。 FIN 表示關閉連接 SYN與FIN是不會同時為1的。 ACK 表示響應 TCP的幾次握手就是通過這樣的ACK表現(xiàn)出來的。 PSH 表示有 DATA數(shù)據(jù)傳輸 PSH為1表示的是有真正的TCP數(shù)據(jù)包內容被傳遞。 RST 表示連接重置 RST一般是在FIN之后才會出現(xiàn)為1的情況。 URG urgent緊急
1.2、三次握手數(shù)據(jù)包詳細分析
Wireshark(前稱Ethereal)是一個網(wǎng)絡數(shù)據(jù)包分析軟件。網(wǎng)絡數(shù)據(jù)包分析軟件的功能是截取網(wǎng)絡數(shù)據(jù)包,并盡可能顯示出最為詳細的網(wǎng)絡數(shù)據(jù)包數(shù)據(jù)。Wireshark使用WinPCAP作為接口,直接與網(wǎng)卡進行數(shù)據(jù)報文交換。使用技巧
ip.addr == 服務器端ip 演示例子
- 第一次握手請求,如下圖: 客戶端發(fā)送一個TCP,標志位為 SYN,Seq(序列號)=0,代表客戶端請求建立鏈接 。

- 第二次握手請求,服務器發(fā)回數(shù)據(jù)包,標志位為[SYN, ACK],ACK設置為客戶端第一次握手請求的Seq+1,即ACK=0+1=1,在隨機產(chǎn)生一個Seq的數(shù)據(jù)包到客戶端。

- 第三次握手請求,客戶端在次發(fā)送確認數(shù)據(jù)包,標識位為ACK,把服務器發(fā)來的Seq+1,即ACK=0+1,發(fā)送給服務器,服務器成功收到ACK報文段之后,連接就建立成功了。

2.1 瀏覽器緩存
瀏覽器緩存控制機制有兩種:
HTML Meta標簽 (非HTTP協(xié)議定義的緩存機制)
HTTP頭信息 (HTTP協(xié)議定義的緩存機制), 今天主要講解HTTP頭信息控制緩存。
2.1.1 為什么需要瀏覽器緩存?
我們知道通過HTTP協(xié)議,在客戶端和瀏覽器建立連接時需要消耗時間,而大的響應需要在客戶端和服務器之間進行多次往返通信才能獲得完整的響應,這拖延了瀏覽器可以使用和處理內容的時間。這就增加了訪問服務器的數(shù)據(jù)和資源的成本,因此利用瀏覽器的緩存機制重用以前獲取的數(shù)據(jù)就變成了性能優(yōu)化時需要考慮的事情。
首先,思考兩個問題:
在頁面中引入靜態(tài)資源文件(css,img,js等),為什么靜態(tài)資源文件改變后,再次發(fā)起請求還是之前的內容,沒有變化呢?
在使用webpack等一些打包工具時,為什么要加上一串hash碼?
2.1.2 瀏覽器請求流程圖
- 瀏覽器第一次請求流程圖(圖片來源網(wǎng)絡):

- 瀏覽器再次請求時(圖片來源網(wǎng)絡):

2.1.3 重要概念 詳情
- Etag/If-None-Match:Etag/If-None-Match也要配合Cache-Control使用。
Etag:web服務器響應請求時,告訴瀏覽器當前資源在服務器的唯一標識(生成規(guī)則由服務器決定)。通常情況下,ETag更類似于資源指紋(fingerprints),如果資源發(fā)生變化了就會生成一個新的指紋,這樣可以快速的比較資源的變化。
If-None-Match:當資源過期時(使用Cache-Control標識的max-age),發(fā)現(xiàn)資源具有Etage聲明,則再次向web服務器請求時帶上頭If-None-Match (Etag的值)。web服務器收到請求后發(fā)現(xiàn)有頭If-None-Match 則與被請求資源的相應校驗串進行比對,決定返回200或304。
- Last-Modified/If-Modified-Since:Last-Modified/If-Modified-Since要配合Cache-Control使用。
Last-Modified:標示這個響應資源的最后修改時間。web服務器在響應請求時,告訴瀏覽器資源的最后修改時間。If-Modified-Since:當資源過期時(使用Cache-Control標識的max-age),發(fā)現(xiàn)資源具有Last-Modified聲明,則再次向web服務器請求時帶上頭 If-Modified-Since,表示請求時間。web服務器收到請求后發(fā)現(xiàn)有頭If-Modified-Since 則與被請求資源的最后修改時間進行比對。若最后修改時間較新,說明資源又被改動過,則響應整片資源內容(寫在響應消息包體內),HTTP 200;若最后修改時間較舊,說明資源無新修改,則響應HTTP 304 (無需包體,節(jié)省瀏覽),告知瀏覽器繼續(xù)使用所保存的cache。
Expires策略:Expires是Web服務器響應消息頭字段,在響應http請求時告訴瀏覽器在過期時間前瀏覽器可以直接從瀏覽器緩存取數(shù)據(jù),而無需再次請求。不過Expires 是HTTP 1.0的東西,現(xiàn)在默認瀏覽器均默認使用HTTP 1.1,所以它的作用基本忽略。Expires 的一個缺點就是,返回的到期時間是服務器端的時間,這樣存在一個問題,如果客戶端的時間與服務器的時間相差很大(比如時鐘不同步,或者跨時區(qū)),那么誤差就很大,所以在HTTP 1.1版開始,使用Cache-Control: max-age=秒替代。Cache-control策略(重點關注):Cache-Control與Expires的作用一致,都是指明當前資源的有效期,控制瀏覽器是否直接從瀏覽器緩存取數(shù)據(jù)還是重新發(fā)請求到服務器取數(shù)據(jù)。只不過Cache-Control的選擇更多,設置更細致,如果同時設置的話,其優(yōu)先級高于Expires。
值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age
| Cache-Control值 | 說明 | 特性 |
|---|---|---|
| Public | 指示響應可被任何緩存區(qū)緩存。 | 可緩存性 |
| Private | 指示對于單個用戶的整個或部分響應消息,不能被共享緩存處理。 這允許服務器僅僅描述當用戶的部分響應消息, 此響應消息對于其他用戶的請求無效。 | 可緩存性 |
| no-cache | 指示請求或響應消息不能緩存,該選項并不是說可以設置 ”不緩存“,切勿望文生義~~ | 可緩存性 |
| max-age | 指示客戶機可以接收生存期不大于指定時間(以秒為單位)的響應。 | 到期 |
| s-maxage | 會代替max-age,只有在代理服務器(nginx代理服務器)才會生效 | 到期 |
| max-stale | 是發(fā)起請求方主動帶起的一個頭,是代表即便緩存過期, 但是在max-stale這個時間內還可以使用過期的緩存, 而不需要愿服務器請求新的內容 | 到期 |
| must-revalidate | 如果max-age設置的內容過期,必須要向服務器請求重新獲取 數(shù)據(jù)驗證內容是否過期 | 驗證 |
| proxy-revalidate | 主要用在緩存服務器,指定緩存服務器在過期后重新從原服務器獲取, 不能從本地獲取 | 驗證 |
| min-fresh | 指示客戶機可以接收響應時間小于當前時間加上指定時間的響應。 | |
| no-store | 用于防止重要的信息被無意的發(fā)布。在請求消息中發(fā)送將 使得請求和響應消息都不使用緩存,完全不存下來。 | |
| no-transform | 主要用于proxy服務器,告訴代理服務器不要隨意改動返回的內容 |
Cache-control 案例
// cache-control.html
<html>
<head>
<meta charset="utf-8" />
<title>cache-control</title>
</head>
<body>
<script src="/script.js"></script>
cache
</body>
</html>
// cache-control.js
?
const http = require('http');
const fs = require('fs');
const port = 3010;
?
http.createServer((request, response) => {
console.log('request url: ', request.url);
?
if (request.url === '/') {
const html = fs.readFileSync('cache.html', 'utf-8');
response.writeHead(200, {
'Content-Type': 'text/html',
});
?
response.end(html);
} else if (request.url === '/script.js') {
response.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=200',
// 'Cache-Control': 'no-cache',
});
?
response.end("console.log('script load !! -- 1')");
}
?
}).listen(port);
?
console.log(`Server started at http://localhost:${port}`)
控制臺運營結果: 第一次請求了script.js ,而第二次沒有。
指請求了/ 并沒有請求 /script.js

瀏覽器結果

以上結果瀏覽器并沒有返回給我們服務端修改的結果,這是為什么呢?是因為我們請求的url /script.js沒有變,那么瀏覽器就不會經(jīng)過服務端的驗證,會直接從客戶端緩存去讀,就會導致一個問題,我們的js靜態(tài)資源更新之后,不會立即更新到我們的客戶端,這也是前端開發(fā)中常見的一個問題,我們是希望瀏覽器去緩存我們的靜態(tài)資源文件(js、css、img等)我們也不希望服務端內容更新了之后客戶端還是請求的緩存的資源, 解決辦法也就是我們在做js構建流程時,把打包完成的js文件名上根據(jù)它內容hash值加上一串hash碼,這樣你的js文件或者css文件等內容不變,這樣生成的hash碼就不會變,反映到頁面上就是你的url沒有變,如果你的文件內容有變化那么嵌入到頁面的文件url就會發(fā)生變化,這樣就可以達到一個更新緩存的目的,這也是目前前端來說比較常見的一個靜態(tài)資源方案。