網頁即時通訊技術深度解析指南 完整指南:掌握即時通訊原理,從 Socket.IO 應用到物聯網協定實戰

me
林彥成
2022-09-23 | 5 min.
文章目錄
  1. 1. 什麼是網頁即時通訊技術?
  2. 2. 即時通訊原理:單工、半雙工與全雙工通訊
  3. 3. Long-Polling:運用 AJAX 模擬即時通訊的傳統方案
  4. 4. Server Sent Events (SSE):單向推送伺服器事件的實作
  5. 5. WebSocket:實現低延遲、全雙工即時通訊的標準
    1. 5.1. Socket.IO 簡介:Event-Based 的通訊框架
    2. 5.2. MQTT:物聯網應用的輕量級通訊協定
  6. 6. FAQ:網頁即時通訊常見問題
    1. 6.1. Q1:WebSocket 與 HTTP 請求最大的差別是什麼?
    2. 6.2. Q2:什麼時候該選 SSE 而不是 WebSocket?
    3. 6.3. Q3:Socket.IO 與原生 WebSocket 有什麼不同?

什麼是網頁即時通訊技術?

網頁即時通訊技術 (Real-time Web Communication) 是指一組允許伺服器與客戶端之間實現「低延遲資料同步」的技術方案。其演進從傳統透過 AJAX 模擬的 Long-Polling (長輪詢),發展到單向推送的 SSE (Server Sent Events),以及現代主流的 WebSocket (全雙工通訊)。這類技術打破了「由客戶端主動請求」的傳統模式,讓伺服器能主動將最新訊息(如聊天訊息、即時股價或 IoT 狀態)推送給用戶。選擇合適的即時通訊技術是提升網頁互動性、構建響應式應用程式的核心關鍵。


在另外一篇文章小編介紹了怎麼透過 非同步 AJAX 的方式 跟伺服器進行溝通,那需要即時的同步溝通怎麼辦?本篇 網頁即時通訊技術 指南將帶您深入了解現代網頁如何實現高效的資料同步。

這篇文章接著會介紹可以做到 網頁即時通訊 服務的主流技術:

  • Long-Polling:長時間輪詢。
  • Server Sent Events:伺服器傳送事件 (SSE)。
  • WebSocket:全雙工通訊。
  • MQTT:輕量級物聯網通訊協定。

API 在系統設計上是為了溝通而產生的,而非同步溝通技術較為簡單且容易實作,即時通訊 則需要較多層面的情境與技術考量。

以男女之間來說,非同步的溝通比較容易造就產生時間管理大師,即時通訊則相對較為困難。

即時通訊原理:單工、半雙工與全雙工通訊

在 2010 年 Chrome 開始支援了新的即時通訊 API 後 Web App 開始走向全新的時代,即時通訊原理 從雙方通訊的方式可以分成下面三種:

  • 單工:訊號只在一個方向上進行傳遞,像是寫情書給喜歡的女生。
  • 雙工:允許雙向資料傳輸,像是曖昧期的相處。
    • 半雙工:可切換方向的單工通訊,像是只有一方有意思的時候,訊息通常是單向的。
    • 全雙工:現代 即時通訊,即將熱戀中的男女,雙方同時接收或是傳送訊息。

非同步溝通:屬於單工或半雙工,注重的會是資訊的 "傳入" 以及 "傳出"。
即時通訊:全雙工,注重的會是 "監聽事件" 以及 "發出事件"。

Long-Polling:運用 AJAX 模擬即時通訊的傳統方案

從翻譯來看就是比較長的 Polling,Long-Polling 的用途其實是以舊的 AJAX 技術模擬 即時通訊 的效果。

  • Polling: 前端向後端發出請求,如果沒拿到想要的資料就重發,伺服器負載較重。
  • Long-Polling:
    1. Client 發 Request 給 Server。
    2. Server 送 Response 給 Client 後才斷開連線 (降低伺服器負擔但占用連線數)。
    3. Client 收到後再發 Request 給 Server。

Polling 像是奪命連環 Call 會一直 Call 到有反應為止,就像正妹的 Line 打開永遠都是 999+ 未讀未接一樣,負擔其實很大。Long-Polling 會是優化版本的,正妹雖然有好幾個通訊軟體,雖然打 Line 過去後被 Mute (因為切去用 Messenger),但至少可以確定之後會回。

以下為之前我練習 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
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 (SSE):單向推送伺服器事件的實作

網頁一般來說是由客戶端向伺服器請求資料。藉由 Server Sent Events (SSE), 伺服器在任何時候都可以向客戶端推送資料,推送進來的訊息可以在客戶端上做事件與資料的處理。

像是現在 LLM 的聊天過程大多較適合用這種做法,一有部分結果時就逐步回傳給問問題的使用者,方便提早確認是否要繼續。

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
41
42
43
44
// Server
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);
});
});

// client
const events = new EventSource("/listen?clientId=test");
events.onmessage = (event) => {
if (event.data) console.log(event.data);
};

events.addEventListener("ping", function (event) {
if (event.data) console.log(event.data);
});

WebSocket:實現低延遲、全雙工即時通訊的標準

WebSocket 這個 API 在不必 Polling 伺服器的情況下,讓用戶傳送訊息至伺服器並接受事件驅動回應,達到低延遲、全雙工的效果。接近真的談戀愛的溝通,雙方各自在沒什麼負擔的情況下進行訊息的交流。

Socket.IO 簡介:Event-Based 的通訊框架

Socket.IO 屬於 Node.js 解決方案,封裝了 Long-PollingWebSocket,是一個 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 = "";
}
});

MQTT:物聯網應用的輕量級通訊協定

MQTT (Message Queuing Telemetry Transport) 適合輕量級物聯網使用,封包較小可以支援大量的 Client。主要是以 TCP/IP 協定上去優化且取代 HTTP 這種較肥的資料傳輸協定,因此會需要一個訊息中介軟體 (MQTT Broker) 來提供解決方案。

底下官方範例會是使用官方提供的 MQTT Broker mqtt://test.mosquitto.org,若為自己的服務需要自行架設。

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
<html>
<head>
<title>test Ws mqtt.js</title>
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
</head>
<body>
<script>
const mqtt = require("mqtt");
const client = mqtt.connect("mqtt://test.mosquitto.org");

client.on("connect", function () {
client.subscribe("presence", function (err) {
if (!err) {
client.publish("presence", "Hello mqtt");
}
});
});

client.on("message", function (topic, message) {
// message is Buffer
console.log(message.toString());
client.end();
});
</script>
</body>
</html>

FAQ:網頁即時通訊常見問題

Q1:WebSocket 與 HTTP 請求最大的差別是什麼?

A:HTTP 是「一問一答」且由客戶端發起的(Request-Response);而 WebSocket 是持久性的連結,一旦握手成功,伺服器與客戶端即可「隨時且雙向」發送訊息,不需要每次都重新傳送標頭 (Header) 資訊,大幅降低了 overhead。

Q2:什麼時候該選 SSE 而不是 WebSocket?

A:如果您的應用場景是「純單向推送」(例如:新聞跑馬燈、即時比分、系統監控面板),SSE 是更簡單且輕量的選擇。它基於標準 HTTP 協議,具備自動斷線重連機制,且對伺服器資源消耗較 WebSocket 低。只有在需要「高頻率雙向互動」(如:線上聊天室、共編文件)時,才推薦使用 WebSocket

Q3:Socket.IO 與原生 WebSocket 有什麼不同?

A:Socket.IO 是一個高階封裝庫。它不僅支援 WebSocket,還內建了自動重連、房間分組 (Rooms)、以及在環境不支援 WebSocket 時自動降級 (Fallback) 為 Long-Polling 的功能。對於快速開發具備強韌性的即時應用,Socket.IO 能省去許多底層維護的麻煩。



喜歡這篇文章,請幫忙拍拍手喔 🤣