這篇文章會介紹以下幾種可以做到即時網頁通訊服務的技術:
- Polling: 前端向 Server 發出 Request,如果沒拿到想要的資料,就再重發,伺服器附載較重。
- Long-Polling: Client 發 Request 給 Server,Server 送 Response 給 Client 後才斷開連線,Client 收到,再發 Request 給 Sevrer,占用連線數。
- Server Sent Events: 由瀏覽器幫我們處理,前端實作上只要監聽相關的事件就可以了。
- WebSocket: 解決了單向請求的問題,但很多情況不一定需要用到雙向通訊,從上面的特性看起來,伺服器也因為使用的情境不同,最佳化時會有差異。
- Forever Frame: IE only,嵌入一個 IFrame,連向 SignalR 提供的內容。
概念上從雙方通訊的方式可以分成下面三種:
- 單工:訊號只在一個方向上進行傳遞,像是郵差寄信。
- 半雙工:可以切換方向的單工通訊,像是小時候玩的對講機。
- 全雙工:現在的即時通訊,雙方都可以同時接收或是傳送訊息。
前端解決方案
在網頁端提供支援較也常用的解決方案有以下四種:
Long-Polling 實作
以下為 Long-Polling 實作範例程式碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| let timeout;
function valueChanged(value) { return (dispatch) => { dispatch(loadSuggestionsInProgress()); dispatch({ type: "VALUE_CHANGED", payload: { value, }, });
timeout && clearTimeout(timeout);
timeout = setTimeout( () => { axios .get(`/suggestions?q=${value}`) .then((response) => dispatch(loadSuggestionsSuccess(response.data.suggestions)) ) .catch(() => dispatch(loadSuggestionsFailed())); }, 1000, value ); }; }
|
Server Sent Events 實作
以下為 Server Sent Events 實作範例程式碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| let clients = []; const server = express();
function sendEventsToClients() { clients.forEach((c) => { c.res.write(`change`); }); }
async function modify(req, res) { res.json({ status: 1 }); return sendEventsToClients(); }
server.get("/modify", modify); server.get("/listen", (req, res) => { const { clientId } = req.query; const headers = { "Content-Type": "text/event-stream", Connection: "keep-alive", "Cache-Control": "no-cache", }; res.writeHead(200, headers); res.write(""); const newClient = { id: clientId, res, }; clients.push(newClient); req.on("close", () => { clients = clients.filter((c) => c.id !== clientId); }); });
const events = new EventSource("/listen?clientId=test"); events.onmessage = (event) => { if (event.data) console.log(event.data); };
|
Socket.IO 簡介
是一個 event-based 全雙工的通訊函式庫,事件驅動這個部分是最容易出包的地方,當我們在和 react 專案整合在一起的時候,就需要去注意事件是不是有和畫面的渲染綁在一起,意思就是說不可以每渲染一次,我就重新建立連結、重新監聽事件、重新發出訊息,這樣一來記憶體很快就會用完,處理器也根本來不及處理。
一個後端的基本範例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const express = require("express"); const app = express(); const http = require("http"); const server = http.createServer(app); const { Server } = require("socket.io"); const io = new Server(server);
app.get("/", (req, res) => { res.sendFile(__dirname + "/index.html"); });
io.on("connection", (socket) => { console.log("a user connected"); socket.on("message", (msg) => { console.log("message: " + msg); }); });
server.listen(3000, () => { console.log("listening on *:3000"); });
|
前端
1 2 3 4 5 6 7 8 9 10 11 12
| var socket = io();
var form = document.getElementById("form"); var input = document.getElementById("input");
form.addEventListener("submit", function (e) { e.preventDefault(); if (input.value) { socket.emit("message", input.value); input.value = ""; } });
|
喜歡這篇文章,請幫忙拍拍手喔 🤣