前端必須懂的計算機網路知識系列文章:
- DNS伺服器和跨域問題
- 計算機網路的分層模型
- IP地址和MAC地址
- 前端必須懂的計算機網路知識—(跨域、代理、本地儲存)
- 前端必須懂的計算機網路知識—(TCP)
- 前端必須懂的計算機網路知識—(HTTP)
- 前端必須懂的計算機網路知識—(XSS、CSRF和HTTPS)
網路模型資料處理過程
域名的空間結構:
由於IP地址不方便記憶,所以同樣用具有層次和唯一性的域名和IP一一對映DNS查詢過程
- 客戶端向本地域名伺服器發出請求,請求www.baidu.com的IP地址
- 本地DNS伺服器向DNS根伺服器發出請求,根DNS伺服器會告訴本地伺服器(.com)的伺服器地址
- 本地DNS伺服器會向(.com域)發請求,會得到(baidu.com)的伺服器地址
- 本地DNS伺服器會向(baidu.com)發請求,會得到(www.baidu.com)的IP地址61.135.169.125
- 本地DNS伺服器向客戶端回覆域名(www.baidu.com)對應的IP地址是61.135.169.125
同源策略和跨域
瀏覽器只對網路請求有同源限制,同源就是協議、域名和埠號一致,不同源的客戶端指令碼在沒有明確授權的情況下,不能讀寫對方XHR資源,反之不同源指令碼讀取對方XHR資源就是跨域。以http://www.a.com/test/index.html 的同源檢測舉例:
- www.a.com/dir/page.ht… ----成功
- child.a.com/test/index.… ----失敗,域名不同
- www.a.com/test/index.… ----失敗,協議不同(https)
- www.a.com:8080/test/index.… ----失敗,埠號不同
跨域的解決方案
- jsonp:只支援GET,不支援POST請求,不安全XSS
- postMessage:配合使用iframe,需要相容IE6、7、8、9
- document.domain:僅限於同一域名下的子域
- cors:需要後臺配合進行相關的設定
- websocket:需要後臺配合修改協議,不相容,需要使用socket.io
- proxy:使用代理去避開跨域請求,需要修改nginx、apache等的配置
jsonp
- 瀏覽器對script標籤src屬性、link標籤ref屬性和img標籤src屬性沒有同源策略限制,利用這個“漏洞”就可以很好的解決跨域請求,JSONP就是利用了script標籤無同源限制的特點來實現的。
- 當向第三方站點請求時,我們可以將此請求放在script標籤的src屬性裡,這就如同請求一個普通的JS指令碼,可以自由的向不同的站點請求。
//建立script傳送請求
//請求返回執行cb函式,並且刪除建立的script
//類似於$ajax中的jsonp
function jsonp(url,params,cb){
return new Promimse((resolve,reject)=>{
window[cb] = function(data){
resolve(data);
document.body.removeChild(script);
}
params={...params,cb},
let arrs=[];
for(let key in params){
arrs.push(`${key}=${params[key]}`)
}
let script = document.createElement('script');
script.src= url + '?'+ arrs.join('&');
document.body.appendChild(script);
})
}
jsonp({
url:'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su',
params:{wd:%E8%B7%A8%E5%9F%9F},
cb:'show'}).then(data=>{
console.log(data)
})
複製程式碼
- 輸入https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=%E8%B7%A8%E5%9F%9F&&cb=show
- 返回show({q:"跨域",p:false,s:["跨域請求","跨域訪問","跨域問題","跨越速運單號查詢","跨域ajax","跨越物流","跨越","跨域速運","跨域請求解決方案","跨域問題怎麼解決"]});
postMessage
配合iframes使用,假設a.html位於服務localhost:3000,b.html位於伺服器localhost:4000
//a.html
<body>
<iframe id="frame" src="http://localhost:4000/b.html" frameborder="0" onload="load()"></iframe>
<script>
function load(){
let frame = document.getElementById('frame');
frame.contentWindow.postMessage('我很帥','http://localhost:4000');
window.onmessage =function (e){
console.log(e.data);
}
}
</script>
</body>
//otherWindow.postMessage(message, targetOrigin);
//otherWindow:指目標視窗,也就是給哪個window發訊息,是 window.frames 屬性的成員或者由 window.open 方法建立的視窗
//message:是要傳送的訊息,型別為 String、Object (IE8、9 不支援)
//targetOrigin: 是限定訊息接收範圍,不限制請使用'*'
//注意otherWindow和targetOrigin的區別
複製程式碼
//b.html
<body>
<script>
//data:訊息
//origin:訊息來源地址
//source:源DOMWindow 物件
window.onmessage =function (e){
console.log(e.data);
e.source.postMessage('不要臉',e.origin);
}
</script>
</body>
複製程式碼
document.domain
//a.html
<body>
helloa
<iframe id="frame" src="http://www.kongbz.com/b.html" frameborder="0" onload="load()"></iframe>
<script>
document.domain = 'kongbz.com';//設定domain
function load(){
let frame = document.getElementById('frame');
console.log(frame.contentWindow.a)
}
</script>
</body>
複製程式碼
<body>
hellob
<script>
document.domain = 'kongbz.com';//設定domain
var a = 'isB'
</script>
</body>
複製程式碼
websocket
客戶端傳送資訊給服務端,如果想實現客戶端向客戶端通訊,只能通過頁面->服務端->另一個頁面
//客戶端
<body>
hellob
<script>
let socket = new WebSocket('ws://localhost:3000');
socket.onopen = function(){
socket.send('我很帥')
}
socket.onmessage = function(e){
console.log(e.data)
}
</script>
</body>
複製程式碼
//服務端
let express = require('express');
let Websocket = require('wss');
let wss= new WebSocket.Server({port:3000})
wss.on('connection',function(ws){
ws.on('message',function(data){
console.log(data);
ws.send('不要臉');
})
})
let app = new express();
app.listen(3000)
複製程式碼
cors
const http = require('http')
const whitList = ['http://localhost:4000'];
http.createServer(function (req, res) {
let origin = req.headers.origin;
//在白名單中的域名才能訪問
if(whitList.includes(origin)){
//允許的域名(* 所有域),*不能和Access-Control-Allow-Credentials一起使用
res.header("Access-Control-Allow-Origin", "*");
//允許攜帶哪個頭訪問,不設定不能攜帶引數
res.header("Access-Control-Allow-Headers","ContentType");
//允許的方法,不設定預設支援GET、HEAD、POST,其他型別必須設定才能處理請求
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
//執行攜帶cookie,設定之後還能伺服器才能接受cookie
res.header("Access-Control-Allow-Credentials",true);
//允許前端獲取哪個頭,不設定瀏覽器不能解析後臺返回的引數
res.header("Access-Control-Allow-Expose-Headers",'ContentType');
if(req.method=== 'OPTIONS'){
res.end()
}
}
}).listen(9000, function () {
console.log('server is runing at 9000')
})
複製程式碼
proxy
例如test.a.cn/index.html頁面去呼叫test.b.cn/service.json
//nginx.conf
location / {
root;
index index.html index.htm;
}
location ~.*\.json {
root json;
add_header "Access-Control-Allow-Origin" "*";
}
複製程式碼
代理和閘道器
- 代理是一種有轉發功能的應用程式,它扮演了位於伺服器和客戶端'中間人'的角色,接收客戶端傳送的請求不改變請求的URI並轉發給伺服器,同時也接收伺服器返回的相應並轉發客戶端
- 快取代理:代理轉發響應時會預先將資源快取在代理伺服器上當代理再次接收到對相同資源的請求時,就可以不從源伺服器那裡獲取資源,而是將之前快取的資源作為響應返回
- 透明代理:轉發請求或響應時,不對報文做任何加工的代理型別
- 閘道器是轉發其他伺服器通訊資料的伺服器,接收從客戶端傳送來的請求時,它就像自己擁有資源的源伺服器一樣對請求進行處理,其工作機制和代理類似,而閘道器能使通訊線路上的伺服器提供非HTTP協議的服務。
反向代理
- 大家都有過這樣的經歷,撥打10086客服電話,可能一個地區的10086客服有幾個或者幾十個,你永遠都不需要關心在電話那頭的是哪一個,叫什麼,男的,還是女的,漂亮的還是帥氣的,你都不關心,你關心的是你的問題能不能得到專業的解答,你只需要撥通了10086的總機號碼,電話那頭總會有人會回答你,只是有時慢有時快而已。那麼這裡的10086總機號碼就是我們說的反向代理。客戶不知道真正提供服務人的是誰。
- 反向代理隱藏了真實的服務端,當我們請求 www.baidu.com 的時候,就像撥打10086一樣,背後可能有成千上萬臺伺服器為我們服務,但具體是哪一臺,你不知道,也不需要知道,你只需要知道反向代理伺服器是誰就好了,www.baidu.com 就是我們的反向代理伺服器,反向代理伺服器會幫我們把請求轉發到真實的伺服器那裡去。Nginx就是效能非常好的反向代理伺服器,用來做負載均衡。
負載均衡和動靜分離
- 負載均衡是反向代理的一種,後端多臺伺服器,nginx根據權重、壓力、頻寬的分配伺服器,避免等待和擁塞
- 動靜分離是反向代理的一種,後端伺服器分為動態資源伺服器和靜態資源伺服器,nginx會根據請求分配伺服器,區分處理邏輯,加快響應
cookie、localstroage、sessionstroage的區別
- Cookie適合儲存一些session資訊:
- cookie限制大小,約4k左右,不適合儲存業務資料,尤其是資料量較大的值
- 存在有效期,到期自動銷燬
- cookie會每次隨http請求一起傳送,浪費寬
- cookie設定了domain可以在子域共享跨域
- 可以使用爬蟲抓取
- localstroage適合儲存應用共享的地址資訊等:
- 儲存資料量大,5M或者更大
- 有效期為永久
- 不會隨http請求一起傳送
- 不能跨域,但是可以使用postMessage和iframe消除這個影響,例如:cross-storage
- 在瀏覽器的隱私模式下不能讀取
- 不能被爬蟲讀取
- sessionstroage適合儲存瀏覽狀態等:
- 儲存資料量大,5M或者更大
- 有效期為到瀏覽器關閉
- 不會隨http請求一起傳送
- 不能被爬蟲讀取
結語
IT即網際網路技術,從事的工作和網路有很大的關係,前端要負責和後臺(伺服器)進行互動,其必然得經過網路,所以懂點網路知識有很大的幫助。接下來會介紹:
- TCP
- HTTP
- HTTPS