什麼是 webSocket
WebSocket 是一種在單個 TCP 連線上進行全雙工通訊的協議。使得客戶端和伺服器之間的資料交換變得更加簡單,允許服務端主動向客戶端推送資料。
在 WebSocket API 中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以建立永續性的連線,並進行雙向資料傳輸。
WebSocket 解決了什麼問題:
在不使用 WebSocket 時,如果我們需要建立一條長連線,有以下幾種方法:
- 輪詢
- 長輪詢(常用)
- SSE(Server Send Event)
當出現類似體育賽事、聊天室、實時位置之類的場景時,客戶端要獲取伺服器端的變化,就只能通過輪詢(定時請求)來了解伺服器端有沒有新的資訊變化。WebSocket 的出現,讓伺服器端可以主動向伺服器端傳送資訊,使得瀏覽器具備了實時雙向通訊的能力,這就是 WebSocket 解決的問題
- 頻寬問題:WebSocket 相對於 HTTP 來說協議頭更加小,同時按需傳遞。
- 資料實時性問題:WebSocket 相對於輪詢和長輪詢來說,能夠實時傳遞資料,延遲更小。
- 狀態問題:相較於 HTTP 的無狀態請求,WebSocket 在建立連線後能夠維持特定的狀態。
WebSocket 與 HTTP 對比
基本使用
客戶端
const ws = new WebSocket('ws://localhost:8888')
ws.onopen = () => {
console.log('WebSocket onopen')
}
ws.onmessage = e => {
console.log(e)
console.log(e.data)
}
ws.onclose = e => {
console.log('WebSocket onclose')
}
ws.onerror = e => {
console.log('WebSocket onerror')
}
複製程式碼
- WebSocket.onopen: 連線成功後呼叫
- WebSocket.onmessage: 當接收到伺服器訊息時呼叫
- WebSocket.onclose: 連線關閉後呼叫
- WebSocket.onerror: 發生錯誤後呼叫
服務端例子(koa)
const Koa = require('koa')
const WebSocket = require('ws')
const app = new Koa()
const ws = new WebSocket.Server({ port: 8888 })
ws.on('connection', ws => {
console.log('server connection')
ws.on('message', msg => {
console.log('server receive msg:', msg)
})
ws.send('Information from the server')
})
app.listen(3000)
複製程式碼
WebSocket 可以傳遞 String、ArrayBuffer 和 Blob 三種資料型別,因此在收到訊息時可能是其中的任意一種。其中,String 和 ArrayBuffer 使用的最多。
- 如果是 String 型別,直接通過字串處理函式即可進行相關轉換,如 JSON 等格式。
- 如果是二進位制 blob 型別,則需要使用 ArrayBuffer 和 DataView 來進行處理,下面簡單介紹。
二進位制資料包括:blob 物件和 Arraybuffer 物件,所以我們需要分開來處理。
// 接收資料
ws.onmessage = function(event) {
if (event.data instanceof ArrayBuffer) {
// 判斷 ArrayBuffer 物件
}
if (event.data instanceof Blob) {
// 判斷 Blob 物件
}
}
// 傳送 Blob 物件的例子
let file = document.querySelector('input[type="file"]').files[0]
ws.send(file)
// 傳送 ArrayBuffer 物件的例子
var img = canvas_context.getImageData(0, 0, 400, 320)
var binary = new Uint8Array(img.data.length)
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i]
}
ws.send(binary.buffer)
複製程式碼
webSocket.bufferedAmount 屬性,表示還有多少位元組的二進位制資料沒有傳送出去 如果傳送的二進位制資料很大的話,可以這樣判斷
var data = new ArrayBuffer(10000000)
socket.send(data)
if (socket.bufferedAmount === 0) {
// 傳送完畢
} else {
// 傳送還沒結束
}
複製程式碼
總結 WebSocket 的優點
- 雙向通訊(一開始說的,也是最重要的一點)。
- 資料格式比較輕量,效能開銷小,通訊高效
- 協議控制的資料包頭部較小,而 HTTP 協議每次通訊都需要攜帶完整的頭部
- 更好的二進位制支援
- 沒有同源限制,客戶端可以與任意伺服器通訊
- 與 HTTP 協議有著良好的相容性。預設埠也是 80 和 443,並且握手階段採用 HTTP 協議,因此握手時不容易遮蔽,能通過各種 HTTP 代理伺服器