WebSocket 前端得懂一點
最近遇到的一個案子,需要做一個像聊天室的功能,因為有幾位後端之前有研究 SignalR,因此就是他們給了 Augutsut 一段範例碼後,August 就拿來套用而已,沒有說很了解。
後來漸漸被提到這個聊天室想要新增很多功能,大家初步討論了一下,都說需要用到 WebSocket,不然前端就要傻傻的一直 POST 給 Server。
因為之後會實際應用,就決定來看一下 WebSocekt API,並整理成這篇筆記文,之後要用的時候方便查找。
WebSocket 重點心智圖
Google 了一下 MDN 跟菜鳥教程後,把吸收到的 WebSocket 基本應用整理成了一個心智圖,如下:
以下就是實際來使用一下。
用 Node.js 建一個簡單的 WebSocket Server
因為 WebSocket 的一個特色就是 Server 可以主動發訊息給Client端,因此就必須要建立一個 WebSocket Server 才能夠測試。
Node.js 有一個「WS」的套件,可參考官方說明:ws: a Node.js WebSocket library
安裝 ws
新增一個資料夾,用終端機開啟後輸入:npm init -y,按下 Enter。
接著再輸入:npm install ws
,按下 Enter,就安裝完成了。
建立 WS Server 基本程式碼
這段直接 copy 說明文件的,新增一個 index.js 檔案,貼上以下程式碼:
const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', function connection(ws) { console.log('server connection') ws.on('message', function incoming(message) { console.log('received: %%s', message); }); ws.send('something'); });
因為 August 有裝 Nodemon,因此在終端機輸入:nodemon index.js
,按下 Enter,WS Server 就 on 起來了。
成功的話,我們建立出的 WS Server URL 就會是這樣:
ws://localhost:8080/
當 Client 端連上後,就會看見終端機印出訊息,像這樣:
前端連結 WebSocket
建立好了 WebSocket Server,接著就可以開始實做前端連結的部份。
1 建立 WebSocket
建立 WebSocket 的程式碼:
var ws = new WebSocket(url, [protocol]);
url 為必填,就是要填 WS Server URL,像我們上一段最後建立的 ws://localhost:8080/
這樣。
protocol 為選填,有多個的話可以寫成陣列。MDN 上面針對這個值的解譯是:
一個表示協定的字串或者是一個表示協定的字串構成的陣列。這些字串可以用來指定子協定,因此一個伺服器可以實作多個 WebSocket 子協定(舉例來說,你可以讓一個伺服器處理不同種類的互動情形,各情形以 protocol 分別)。若不指定協定字串則預設值為空字串。
建立完 WebSocket 後,我們可以 console.log(ws)
看一下有什麼:
readyState,連結 WS Server 的回應碼,總共有 4 碼,代表意思如下:
- 0 – 表示連接尚未建立。
- 1 – 表示連接已建立,可以進行通信。
- 2 – 表示連接正在進行關閉。
- 3 – 表示連接已經關閉或者連接不能打開。
所以像上面截圖中顯示的是「1」,代表成功連結上了 WS Server。
bufferedAmount,代表 Client 端要傳給 Server 的訊息已被 send() 放入正在隊列中等待傳輸,但是還沒有發出。
onopen、onerror、onclose、onmessage,這 4 個是連接 WS Server 後會有的事件,介紹於下一段。
2 監聽 WebSocket 事件
當連結上 WS Server 後,Server 就可以主動發訊息給前端,前端這邊不用一直 POST 去查有沒有什麼更新,只要監聽 Server 有沒有發新的訊息來就可以了。
WebSocket 的事件共有 4 個:onopen、onerror、onclose、onmessage。
open
跟 WS Server 連接建立時會觸發:
ws.addEventListener('open', function() { console.log('連結建立成功。') })
error
通信發生錯誤時觸發。
close
關閉跟 WS Server 的連線時觸發:
ws.addEventListener('close', function() { console.log('連結關閉,咱們下次見~') })
message
收到 Server 發來的訊息時觸發,這是最重要的一個事件,WebSocket 優秀的地方就在這,可以監聽由 Server 主動發來的訊息。
ws.addEventListener('message', function(e) { var msg = JSON.parse(e.data); console.log(msg) })
這個 function 一整個跟 postMessage 好像,所以看到也不會覺得陌生。
之所以要將傳來的 e.data JSON.parse
,原因是 WS Server 傳來的格式會是字串,如果 Server 傳來的格式直接是 JSON 未轉成字串的話,前後端都會報錯。
因為 MDN 特地標了一句「Firefox 目前只支援字串傳送」,所以實際要應用時,建議不論前後端都傳字串就好,畢竟 Firefox 是有一定市佔率的。
August 這邊直接在 Node.js 的部份加入一個 setTimeout
10 秒後傳個訊息出來:
wss.on('connection', function connection(ws) { var data = { name: "August", blog: "Let's Write" } setTimeout(function() { ws.send(JSON.stringify(data)); }, 10000); });
之後在瀏覽器上等了 10 秒就有成功看到,代表 WebSocket 真的可以讓 Server 主動發訊息到前端這。
3 Client 端發訊息給 WS Server
WebSocket 是讓前後端可以互通訊息的,因此除了接收由後端來的訊息,前端也可以主動發訊息給後端。
WebSocket API,methods 有2個:send、close。
send 就是發訊息,close 就是關閉跟 WS Server 的連結。
send
這邊一樣前端 10 秒後發個訊息出去,WS Server 監聽一個 on message 的事件。
前端 發訊息:
setTimeout(function() { var testMsg = {name: "August", blog: "Let's Write"}; ws.send(JSON.stringify(testMsg)) }, 10000)
後端 收訊息:
wss.on('connection', function connection(ws) { ws.on('message', function incoming(message) { console.log(JSON.parse(message)); }); });
測試後,終端機成功看到訊息:
close
前端如果要主動斷開跟 WS Server 的連結,就是用 close。
這邊一樣寫一個在 10 秒後 close,並且加一個監聽 close 的事件:
ws.addEventListener('close', function(e) { console.log(e) }) setTimeout(function() { ws.close() }, 5000)
最後就會看到印出來的訊息是這樣:
筆記後心得
這篇是對 WebSocket API 的筆記整理,實際應用上比較多的情況是用在聊天室,所以後端收到 message 後,要再主動把收到的訊息發給所有有連結的前端,才能讓聊天室的訊息都同步。
所以後端的朋友責任會重一些,也因此才會有相應的套件產生吧?
參考資源
ws: a Node.js WebSocket library
[…] 同場加映,之前寫過的 WebSocket 筆記文:WebSocket 基本介紹及使用筆記。 […]