1,http使用場景:
網頁輸入url
ajax獲取數據
img標籤加載圖片
2,主要技術點:緩存,這是最能優化性能的地方!!!
cache-control:
public,private
must-revalidate
no-chche,no-store
3,有意義的頭
Content-Type,Content-Encoding等用來約束數據類型
Cookie保持會話信息
CORS跨域且保證安全性限制,不讓非法人員使用
4,TCP連接問題:
三次握手
https鏈接創建過程,爲什麼安全
什麼是長鏈接,爲什麼需要長鏈接
http2信道複用爲什麼可以提高性能
一, http請求過程:
這些節點對web開發人員是透明的,但是深入理解可以幫助開發人員優化性能!
(瀏覽器調試工具performance會記錄每個節點的時間點、時間消耗)
流程:
1,startTime之後就要開始跳轉(redirectStart),因爲瀏覽器要判斷地址是否已經永久跳轉至新地址
2,看緩存(cache)
3,DNS解析
4,創建tcp鏈接(三次握手),如果請求是https,就要創建https鏈接,在http基礎上加一個保證數據安全的過程
5,發送數據包
6,返回數據
二, 網絡協議分層:
物理層:
定義物理設備如何傳輸數據(硬件,網卡,網線)
數據鏈路層:
在通信的實體間建立數據鏈路連接(0101之類)
網絡層:
爲數據在節點之間傳輸創建邏輯鏈路(尋找百度的服務器)
傳輸層:
向用戶提供可靠的端到端(End-to-End)服務(從自己電腦到百度鏈接之後,兩端如何傳輸數據,傳輸的方式都在這裏定義。傳輸大數據分包,分片,數據過去之後數據組裝)。傳輸層向高層屏蔽了下層數據通信的細節,http這一層不需要關心。
應用層:
爲應用軟件提供了很多服務;構建與tcp/ip協議之上;屏蔽了網絡傳輸相關細節
三, http歷史:
http/1.0:
1,增加了很多命令,POST,PUT。。。
2,增加了status code,header
3,多字符集支持、多部分發送、權限、緩存
http/1.1:
1,持久連接:http1裏面一個http請求發送就要在客戶端和服務端之間創建一個http連接,傳輸之後就關閉了,過程中有tcp三次握手,消耗性能
2,pipeline:同一個連接裏面可以發送多個請求,服務端發送回來的數據需要等待順序返回,串行。http2進行了優化,變成並行
3,增加了host和其他命令:host可以在同一臺服務器上跑多個web服務,node和java,host判斷提供哪個服務
http/2:
1,所有數據二進制傳輸
2,因爲第一點,所有pipeline可以並行操作,傳輸效率質的提升
3,頭信息壓縮以及推送等提高效率的內容,解決http1.1效率低下的問題
http1.1每次發送都要完整的頭信息,如content-type信息都是字符串, 消耗性能,壓縮之後能節約性能
推送:http請求只能客戶端先發請求,服務器接收請求;http2服務器端可以主動發送請求。比如html頁面,先獲取html信息,才能發送請求獲取css和js數據,現在http2可以主動發送數據,變串行爲並行
https:
安全版本的http1
3.1 HTTP報文
請求報文:
- method:GET,POST(用來定義對資源的操作,GET,POST,PUT,DELETE)
- url:具體請求資源的地方
- 協議:http1.1和http2爲主
響應報文:
- 協議 狀態 狀態代表的具體含義
- 首部跟主體空行隔開
- HTTP CODE:定義服務器對請求的處理結果
(1) 100-199:操作要持續進行,後續才返回數據
(2) 200-299:操作成功
(3) 300-399:操作需要重定向,用別的方式獲取數據
(4) 400-499:操作失敗;401:沒有權限
(5) 500-599:操作存在錯誤
3.2 URI,URL,URN
- uri:Uniform Resource Identifier/統一資源標誌符,名詞的定義爲了識別互聯網上固定位置的資源,包含url和urn
- url:UniForm Rsource Locator/統一資源定位符
比如:http://user:[email protected]:80/path?query=string#hash
默認帶80端口
/是當前文件夾根目錄,現在/path的使用是在代碼中執行
hash:查看文檔某個片段,作爲錨點
此種格式就是url(http,ftp使用) - urn:永久統一資源定位符
內容換地址,通過urn也能訪問到,因爲404之後url上不知道新的地址;目前沒有成熟的方案
3.3 HTTP三次握手
- http沒有連接的概念,只有請求和響應,請求和響應都是數據包,之間要經過傳輸通道,就在tcp裏面創建的客戶端發起,服務端接收的連接,http請求是在這個連接上發送的。
- http1中,在一個http請求時創建tcp連接,請求發送之後,服務器響應,http就斷開了
- http1.1中,可以聲明http連接可以保持連接,
- http2中,http請求可以併發,一個用戶對一個網頁只需要一個tcp連接
之所以需要三次握手,因爲網絡連接有消耗,數據傳輸有延時,數據可能丟失,客戶端如果沒有接收到服務端接收的信息,超時之後會發送新的連接,這樣舊的連接就不會斷開,十分浪費
windows網絡抓包工具:wiresshark
3.4 HTTP客戶端
在命令行中請求網頁數據,處處有驚喜哦:
1,curl baidu.com
2,curl www.baidu.com
3,curl -v www.baidu.com
四, 跨域CORS
實現一個簡單的跨域訪問:
node後端配置跨域
response.writeHead(200,{
'Access-Control-Allow-Origin':'*' //表示所有的域都可以訪問
'Access-Control-Allow-Headers':'X-Test-Cors', //fetch預請求
'Access-Control-Allow-Methods':'POST,PUT,Delete', //除了默認get之外預請求的類型
'Access-Control-Max-Age':'1000', //單位是s
})
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>這是一個test頁面</div>
<script>
var xhr = new XMLHttpRequest()
xhr.open('GET', 'http://127.0.0.1:8887/')
xhr.send()
console.log(11)
</script>
</body>
</html>
server:前端服務代碼
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html)
}).listen(8888)
console.log('server listening on 8888')
server2:後端服務代碼
const http = require('http')
http.createServer(function (request, response) {
console.log('request come', request.url)
response.writeHead(200, {
'Access-Control-Allow-Origin': 'http://127.0.0.1:8888', // 設置可以跨域的服務器,*代表全都可以
'Access-Control-Allow-Headers': 'X-Test-Cors',
'Access-Control-Allow-Methods': 'POST, PUT, DELETE',
'Access-Control-Max-Age': '1000'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
html:
fetch('http://127.0.0.1:8887', {
method: 'POST',
headers: {
'X-Test-Cors': '123' // 會先向後臺發一次預請求,通過之後再發正式請求
}
})
server2:
'Access-Control-Allow-Headers': 'X-Test-Cors', //不配合設置就無法跨域
'Access-Control-Allow-Methods': 'POST, PUT, DELETE', // 這裏也要和html裏面的method對應
'Access-Control-Max-Age': '1000' // 這個請求最長的連接時間,在這個時間段內不需要發送預請求
需要注意的幾點:
- CORS預請求(OPTIONS方法),method默認允許的方法:GET,POST,HEAD
- 默認允許的數據類型Content-Type:
text/plain
multipart/form-data
application/x-www-form-urlencoded - 其他請求頭限制。。。
五, HTTP請求頭以及緩存Cache-Control
重點來了!
public:http請求返回的請求經過的任何路徑(包括代理服務器,瀏覽器)都可以進行緩存
private:只有瀏覽器可以緩存
no-cache:都不能緩存
5.1 Cache-Control緩存時間:
max-age<seconds>:緩存時間
s-maxage<sceonds>:代替max-age,但是隻有在代理服務器纔會生效
max-stale<sceonds>:發起請求方主動攜帶(不是瀏覽器,應該是代碼裏寫的),max-age過期之後,如果max-stale可以代替max-age
嘗試下代碼:
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>這是一個test頁面</div>
<script src="/script.js"></script>
</body>
</html>
server.js:
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
if (request.url === '/') {
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html)
}
if (request.url === '/script.js') {
response.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=20' //設置後,在緩存期內,客戶端會直接在本地緩存中讀取數據,即使後臺修改
})
response.end('console.log("script loaded")')
}
}).listen(8888)
console.log('server listening on 8888')
5.2 Cache-Control重新驗證:
- must-revalidate:max-age過期之後,要到服務器端驗證是否真的過期,而不能直接使用本地緩存
- proxy-revalidate:用在緩存服務器的must-revalidate
5.3 Cache-Control其他操作:
- no-store:和no-cache區分,no-cache是指可以在瀏覽器和代理服務器緩存,但是要去服務器驗證,服務器說可以纔可以使用;no-store是徹底的都不能存儲緩存
- no-transform:proxy服務器使用。不允許代理服務器對返回的內容進行壓縮等改動
注意:這些頭都只是約定,並不是強約束
5.4 Cache-Control資源驗證
- etag等
server.js
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
if (request.url === '/') {
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html)
}
if (request.url === '/script.js') {
const etag = request.headers['if-none-match']
if (etag === '777') {
// 304表示資源未修改,瀏覽器會直接拿第一次訪問獲得的數據
response.writeHead(304, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=2000000, no-cache',
'Last-Modified': '123',
'Etag': '777'
})
response.end('mingming')
}
else {
//第一次訪問時
response.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=2000000, no-cache',
'Last-Modified': '123',
'Etag': '777'
})
response.end('console.log("script loaded twice")')
}
}
}).listen(8888)
console.log('server listening on 8888')
5.5 Cookie和Session
Cookie
- 服務端返回數據的時候通過Set-cookie的header保存在瀏覽器裏面的內容,瀏覽器保存之後下次同域請求會帶上cookie,用來識別用戶
- max-age(多長時間)和expires(到哪個時間點)來設置過期時間
- Secure只在https纔會發送,http沒有
- HttpOnly:無法通過document.cookie訪問,用來避免攻擊,如CSRS攻擊,通過網頁注入腳本拿到用戶cookie到服務器拿到用戶數據
- Set-cookie可以寫多個,有時效限制,不設置過期時間,瀏覽器關閉之後就沒有了
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
if (request.url === '/') {
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html',
'Set-Cookie': 'id=123' //在這裏直接給客戶端設置cookie
'Set-Cookie': ['id=123', 'name=ming'] //設置多個cookie
'Set-Cookie': ['id=123;max-age=4', 'name=333'] //設置時間
'Set-Cookie': ['id=123; max-age=2', 'abc=456;domain=test.com']
//同一個域名下,給其他地址設置cookie,使用demain
//記住,不能給自己的上級域名設置,會跨域
})
response.end(html)
}
}).listen(8888)
console.log('server listening on 8888')
5.6 domain
不同域的cookie不能共享,只能單獨設置,也可以通過設置domain完成二級域名恭共享cookie
演示代碼:代碼溜了
5.7 數據協商
請求:
- Accept:指定需要的數據類型
- Accept-Encoding:限制服務端數據壓縮類型,gzip(最流行),deflate,br(最少 ,以後可能主流);服務端將數據進行壓縮,在客戶端進行解壓,一般超過1kb的時候,傳輸的size會小於解壓之後的size
- Accept-Language:指定返回數據類型
User-Agent:瀏覽器相關信息(指定移動端,PC端)
user-agent:
Mozilla/5.0 // 老的瀏覽器默認的頭;
(Macintosh; Intel Mac OS X 10_15_5) // 電腦系統
AppleWebKit/537.36 (KHTML, like Gecko) // 瀏覽器內核,KHTML渲染引擎,類似於Gecko(火狐瀏覽器引擎)
Chrome/83.0.4103.106 Safari/537.36 // 瀏覽器
返回:
Content-Type:返回的數據格式
content-type: text/html; charset=utf-8 || text/javascript 。。。
Content-Encoding:對應Accept-Encoding
Content-Language:對應Accept-Language
5.8 Redirect
- 302是臨時跳轉
- 301永久跳轉,意味着下次進入的時候url1會直接在瀏覽器更換url2,url1被緩存在瀏覽器中,disk cache,緩存時間很長,除非清緩存,服務器無法主導,所以這個使用301要非常謹慎
const http = require('http')
http.createServer(function (request, response) {
console.log('request come', request.url)
if (request.url === '/') {
response.writeHead(302, { // or 301
'Location': '/new'
})
response.end()
}
if (request.url === '/new') {
response.writeHead(200, {
'Content-Type': 'text/html',
})
response.end('<div>this is content</div>')
}
}).listen(8888)
console.log('server listening on 8888')
5.9 Content-Security-Policy,內容安全策略
作用:
- 限制資源獲取:資源從哪裏獲取,請求發到哪裏
- 報告資源獲取越權:獲取不應該獲取的資源時,給服務進行報告,做出調整
default-src限制全局
特定資源類型,限制資源範圍:connect-src,img-src,script-src。。。
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
if (request.url === '/') {
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html',
// 'Content-Security-Policy': 'script-src \'self\'; form-action \'self\'; report-uri /report'
})
response.end(html)
} else {
response.writeHead(200, {
'Content-Type': 'application/javascript'
})
response.end('console.log("loaded script")')
}
}).listen(8888)
console.log('server listening on 8888')
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; form-action 'self';">
<title>Document</title>
</head>
<body>
<div>This is content</div>
<script>
console.log('inline js')
</script>
<script src="test.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/core.js"></script>
</body>
</html>