javaScript
,HTML
,CSS
.Dom
操做EventLoop
和渲染機制IM
以及超大型高併發網站應用等,例如B站
)在使用某個技術的時候,必定要去追尋原理和底層的實現,久而久之堅持,只要自身底層的基礎紮實,不管技術怎麼變化,學習起來都不會太累,總的來講就是拒絕5分鐘技術css
url
地址,到顯示頁面發生了什麼出發:TCP
連接之上TCP
呢TCP三次握手的過程以下:html
SYN
報文給服務器端,進入SYN_SEND
狀態。SYN
報文,迴應一個SYN
(SEQ=y)ACK(ACK=x+1)報文,進入SYN_RECV狀態。如圖所示:前端
TCP
的四次揮手:java
特別提示:
SYN
報文用來通知,FIN
報文是用來同步的react
以上就是面試官常問的三次握手,四次揮手,可是這不只僅面試題,上面僅僅答到了一點皮毛,學習這些是爲了讓咱們後續方便了解他的優缺點。webpack
TCP
鏈接創建後,咱們能夠有多種協議的方式通訊交換數據:http 1.0
早先1.0的HTTP版本,是一種無狀態、無鏈接的應用層協議。git
HTTP1.0規定瀏覽器和服務器保持短暫的鏈接,瀏覽器的每次請求都須要與服務器創建一個TCP鏈接,服務器處理完成後當即斷開TCP鏈接(無鏈接),服務器不跟蹤每一個客戶端也不記錄過去的請求(無狀態)。github
這種無狀態性能夠藉助cookie/session機制來作身份認證和狀態記錄。而下面兩個問題就比較麻煩了。web
首先,無鏈接的特性致使最大的性能缺陷就是沒法複用鏈接。每次發送請求的時候,都須要進行一次TCP的鏈接,而TCP的鏈接釋放過程又是比較費事的。這種無鏈接的特性會使得網絡的利用率很是低。面試
其次就是隊頭阻塞(headoflineblocking)。因爲HTTP1.0規定下一個請求必須在前一個請求響應到達以前才能發送。假設前一個請求響應一直不到達,那麼下一個請求就不發送,一樣的後面的請求也給阻塞了。
Http 1.0
的致命缺點,就是沒法複用TCP
鏈接和並行發送請求,這樣每次一個請求都須要三次握手,並且其實創建鏈接和釋放鏈接的這個過程是最耗時的,傳輸數據相反卻不那麼耗時。還有本地時間被修改致使響應頭expires
的緩存機制失效的問題~(後面會詳細講)
Http 1.1
,這也是技術的發展必然結果~Http 1.1
出現,繼承了Http1.0
的優勢,也克服了它的缺點,出現了keep-alive
這個頭部字段,它表示會在創建TCP
鏈接後,完成首次的請求,並不會馬上斷開TCP
鏈接,而是保持這個鏈接狀態~進而能夠複用這個通道
Http 1.1
而且支持請求管道化,「並行」發送請求,可是這個並行,也不是真正意義上的並行,而是可讓咱們把先進先出隊列從客戶端(請求隊列)遷移到服務端(響應隊列)
例如:客戶端同時發了兩個請求分別來獲取html和css,假如說服務器的css資源先準備就緒,服務器也會先發送html再發送css。
B站
首頁,就有keep-alive
,由於他們也有IM
的成分在裏面。須要大量複用TCP
鏈接~HTTP1.1
好像仍是沒法解決隊頭阻塞的問題實際上,現階段的瀏覽器廠商採起了另一種作法,它容許咱們打開多個TCP的會話。也就是說,上圖咱們看到的並行,實際上是不一樣的TCP鏈接上的HTTP請求和響應。這也就是咱們所熟悉的瀏覽器對同域下並行加載6~8個資源的限制。而這,纔是真正的並行!
Http 1.1
的致命缺點:咱們也能夠用
dns-prefetch和 preconnect tcp
來優化~
<link rel="preconnect" href="//example.com" crossorigin>
<link rel="dns=prefetch" href="//example.com">
複製代碼
Tip
: webpack
能夠作任何事情,這些均可以用插件實現Http 2.0
HTTP 2.0 在客戶端和服務器端使用「首部表」來跟蹤和存儲以前發送的鍵-值對,對於相同的數據,再也不經過每次請求和響應發送;通訊期間幾乎不會改變的通用鍵-值對(用戶代理、可接受的媒體類型,等等)只 需發送一次。事實上,若是請求中不包含首部(例如對同一資源的輪詢請求),那麼 首部開銷就是零字節。此時全部首部都自動使用以前請求發送的首部。
若是首部發生變化了,那麼只須要發送變化了數據在Headers幀裏面,新增或修改的首部幀會被追加到「首部表」。首部表在 HTTP 2.0 的鏈接存續期內始終存在,由客戶端和服務器共同漸進地更新 。
本質上,固然是爲了減小請求啦,經過多個js或css合併成一個文件,多張小圖片拼合成Sprite圖,可讓多個HTTP請求減小爲一個,減小額外的協議開銷,而提高性能。固然,一個HTTP的請求的body太大也是不合理的,有個度。文件的合併也會犧牲模塊化和緩存粒度,能夠把「穩定」的代碼or 小圖 合併爲一個文件or一張Sprite,讓其充分地緩存起來,從而區分開迭代快的文件。
Demo
的性能對比:![]()
Http
的那些致命缺陷,並無徹底解決,因而有了https
,也是目前應用最廣的協議之一HTTP+ 加密 + 認證 + 完整性保護 =HTTPS
?不加密的重要內容被
wireshark
這類工具抓到包,後果很嚴重~
HTTPS 並不是是應用層的一種新協議。只是 HTTP 通訊接口部分用 SSL(SecureSocket Layer)和 TLS(Transport Layer Security)協議代替而已。 一般,HTTP 直接和 TCP 通訊。
當使用 SSL 時,則演變成先和 SSL 通訊,再由 SSL和 TCP 通訊了。簡言之,所謂 HTTPS,其實就是身披 SSL 協議這層外殼的HTTP。
在採用 SSL 後,HTTP 就擁有了 HTTPS 的加密、證書和完整性保護這些功能。SSL 是獨立於 HTTP 的協議,因此不光是 HTTP 協議,其餘運行在應用層的 SMTP和 Telnet 等協議都可配合 SSL 協議使用。能夠說 SSL 是當今世界上應用最爲普遍的網絡安全術。
加密和解密都會用到密鑰。沒有密鑰就沒法對密碼解密,反過來講,任何人只要持有密鑰就能解密了。若是密鑰被攻擊者得到,那加密也就失去了意義。
Https
加密篇幅太長,這篇文章寫得很好,你們能夠去看看。HTTPS
雖好,非對稱加密雖好,可是不要濫用既然 HTTPS 那麼安全可靠,那爲什麼全部的 Web 網站不一直使用 HTTPS? 其中一個緣由是,由於與純文本通訊相比,加密通訊會消耗更多的 CPU 及內存資源。若是每次通訊都加密,會消耗至關多的資源,平攤到一臺計算機上時,可以處理的請求數量一定也會隨之減小。
所以,若是是非敏感信息則使用 HTTP 通訊,只有在包含我的信息等敏感數據時,才利用 HTTPS 加密通訊。 特別是每當那些訪問量較多的 Web 網站在進行加密處理時,它們所承擔着的負載不容小覷。在進行加密處理時,並不是對全部內容都進行加密處理,而是僅在那些須要信息隱藏時纔會加密,以節約資源。
除此以外,想要節約購買證書的開銷也是緣由之一。 要進行 HTTPS 通訊,證書是必不可少的。而使用的證書必須向認證機構(CA)購買。證書價格可能會根據不一樣的認證機構略有不一樣。一般,一年的受權須要數萬日元(如今一萬日元大約摺合 600 人民幣)。那些購買證書並不合算的服務以及一些我的網站,可能只會選擇採用HTTP 的通訊方式。
請求報文格式
響應報文格式
所謂響應頭,請求頭,其實均可以本身添加字段,只要先後端給對應的處理機制便可
Node.js
代碼實現響應頭的設置if (config.cache.expires) {
res.setHeader("expries", new Date(Date.now() + (config.cache.maxAge * 1000)))
}
if (config.cache.lastModified) {
res.setHeader("last-modified", stat.mtime.toUTCString())
}
if (config.cache.etag) {
res.setHeader('Etag', etagFn(stat))
}
}
複製代碼
Node.js
靜態資源服務器,github.com/JinJieTan/u… star
~首次請求:
非首次請求:
用戶行爲與緩存:
HTTP信息頭中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告訴瀏覽器不用緩存的請求
須要根據Cookie,認證信息等決定輸入內容的動態請求是不能被緩存的
通過HTTPS安全加密的請求(有人也通過測試發現,ie其實在頭部加入Cache-Control:max-age信息,firefox在頭部加入Cache-Control:Public以後,可以對HTTPS的資源進行緩寸)
通過HTTPS安全加密的請求(有人也通過測試發現,ie其實在頭部加入Cache-Control:max-age信息,firefox在頭部加入Cache-Control:Public以後,可以對HTTPS的資源進行緩存,參考《HTTPS的七個誤解》)
POST請求沒法被緩存
HTTP響應頭中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的請求沒法被緩存
websocket
協議開始:傳統的協議沒法服務端主動
push
數據,因而有了這些騷操做:
webSocket
.webSockets
的目標是在一個單獨的持久鏈接上提供全雙工、雙向通訊。在Javascript建立了Web Socket以後,會有一個HTTP請求發送到瀏覽器以發起鏈接。在取得服務器響應後,創建的鏈接會將HTTP升級從HTTP協議交換爲WebSocket協議。
webSocket
原理: 在TCP
鏈接第一次握手的時候,升級爲ws
協議。後面的數據交互都複用這個TCP
通道。
客戶端代碼實現:
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = function () {
ws.send('123')
console.log('open')
}
ws.onmessage = function () {
console.log('onmessage')
}
ws.onerror = function () {
console.log('onerror')
}
ws.onclose = function () {
console.log('onclose')
}
複製代碼
Node.js
語言實現const express = require('express')
const { Server } = require("ws");
const app = express()
const wsServer = new Server({ port: 8080 })
wsServer.on('connection', (ws) => {
ws.onopen = function () {
console.log('open')
}
ws.onmessage = function (data) {
console.log(data)
ws.send('234')
console.log('onmessage' + data)
}
ws.onerror = function () {
console.log('onerror')
}
ws.onclose = function () {
console.log('onclose')
}
});
app.listen(8000, (err) => {
if (!err) { console.log('監聽OK') } else {
console.log('監聽失敗')
}
})
複製代碼
webSocket
的報文格式有一些不同:客戶端和服務端進行Websocket消息傳遞是這樣的:
ping
andpong
Go
實現:package main
import (
"net/http"
"time"
"github.com/gorilla/websocket"
)
var (
//完成握手操做
upgrade = websocket.Upgrader{
//容許跨域(通常來說,websocket都是獨立部署的)
CheckOrigin:func(r *http.Request) bool {
return true
},
}
)
func wsHandler(w http.ResponseWriter, r *http.Request) {
var (
conn *websocket.Conn
err error
data []byte
)
//服務端對客戶端的http請求(升級爲websocket協議)進行應答,應答以後,協議升級爲websocket,http創建鏈接時的tcp三次握手將保持。
if conn, err = upgrade.Upgrade(w, r, nil); err != nil {
return
}
//啓動一個協程,每隔5s向客戶端發送一次心跳消息
go func() {
var (
err error
)
for {
if err = conn.WriteMessage(websocket.TextMessage, []byte("heartbeat")); err != nil {
return
}
time.Sleep(5 * time.Second)
}
}()
//獲得websocket的長連接以後,就能夠對客戶端傳遞的數據進行操做了
for {
//經過websocket長連接讀到的數據能夠是text文本數據,也能夠是二進制Binary
if _, data, err = conn.ReadMessage(); err != nil {
goto ERR
}
if err = conn.WriteMessage(websocket.TextMessage, data); err != nil {
goto ERR
}
}
ERR:
//出錯以後,關閉socket鏈接
conn.Close()
}
func main() {
http.HandleFunc("/ws", wsHandler)
http.ListenAndServe("0.0.0.0:7777", nil)
}
複製代碼
Node.js
實現):this.heartTimer = setInterval(() => {
if (this.heartbeatLoss < MAXLOSSTIMES) {
events.emit('network', 'sendHeart');
this.heartbeatLoss += 1;
this.phoneLoss += 1;
} else {
events.emit('network', 'offline');
this.stop();
}
if (this.phoneLoss > MAXLOSSTIMES) {
this.PhoneLive = false;
events.emit('network', 'phoneDisconnect');
}
}, 5000);
複製代碼
new Socket
開始:SDK
接入,可是逼格高些仍是本身重寫比較好。IM
桌面應用開發的~const {Socket} = require('net')
const tcp = new Socket()
tcp.setKeepAlive(true);
tcp.setNoDelay(true);
//保持底層tcp連接不斷,長鏈接
指定對應域名端口號連接
tcp.connect(80,166.166.0.0)
創建鏈接後
根據後端傳送的數據類型 使用對應不一樣的解析
readUInt8 readUInt16LE readUInt32LE readIntLE等處理後獲得myBuf
const myBuf = buffer.slice(start);//從對應的指針開始的位置截取buffer
const header = myBuf.slice(headstart,headend)//截取對應的頭部buffer
const body = JSON.parse(myBuf.slice(headend-headstart,bodylength).tostring())
//精確截取數據體的buffer,而且轉化成js對象
複製代碼
即時通信強烈推薦使用
Golang
,GRPC
,Prob
傳輸數據。
webpack-electron-react-websocket
的Demo, github.com/JinJieTan/r…以爲寫得不錯,能夠點個贊支持下,文章也借鑑了一下其餘大佬的文章,可是地址都貼上來了~ 歡迎
gitHub
點個star
哦~