
概念概覽
sequenceDiagram participant Client as 客戶端 participant Server as 伺服器 rect rgb(240, 240, 255) Note over Client,Server: 傳統 HTTP(請求-回應) Client->>Server: HTTP Request Server->>Client: HTTP Response Client->>Server: HTTP Request Server->>Client: HTTP Response end rect rgb(240, 255, 240) Note over Client,Server: WebSocket(全雙工) Client->>Server: HTTP Upgrade 請求 Server->>Client: 101 Switching Protocols Client->>Server: 訊息 Server->>Client: 訊息 Server->>Client: 主動推送 Client->>Server: 訊息 end
什麼是 WebSocket?
想像一下:傳統的 HTTP 就像打電話給客服——你問一個問題、對方回答、然後掛斷,下次有問題要再打一通。但如果你需要的是像 LINE 聊天那樣的即時對話呢?WebSocket 就是讓你跟伺服器之間「保持通話不掛斷」的技術。這也是為什麼你在 Slack 或 Discord 上發訊息時,對方能瞬間收到,而不是等幾秒才刷新出來。
WebSocket 是一種網路通訊協定,它提供了全雙工(Full-Duplex) 的通訊管道,讓伺服器和客戶端可以在單一 TCP 連線上同時雙向傳輸資料。
為什麼需要 WebSocket?
傳統的 HTTP 協定是「請求-回應」模式:
- 客戶端發送請求
- 伺服器回應資料
- 連線結束
這種模式的問題是:伺服器無法主動推送資料給客戶端。
如果要實現即時更新,傳統做法是:
- 輪詢(Polling):客戶端每隔幾秒發一次請求 → 浪費資源
- 長輪詢(Long Polling):伺服器保持連線直到有新資料 → 還是不夠即時
WebSocket 解決了這個問題,建立連線後,雙方可以隨時互相發送訊息。
WebSocket 運作原理
1. 握手階段(Handshake)
WebSocket 連線從一個 HTTP 請求開始:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
伺服器同意後回應:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
2. 資料傳輸
握手成功後,連線從 HTTP 升級為 WebSocket 協定,雙方可以自由發送訊息。
3. 連線關閉
任一方都可以發送關閉訊號結束連線。
前端使用範例
// 建立連線
const socket = new WebSocket('wss://example.com/socket');
// 連線成功
socket.onopen = () => {
console.log('已連線');
socket.send('Hello Server!');
};
// 收到訊息
socket.onmessage = (event) => {
console.log('收到:', event.data);
};
// 連線關閉
socket.onclose = () => {
console.log('連線已關閉');
};
// 發生錯誤
socket.onerror = (error) => {
console.error('WebSocket 錯誤:', error);
};常見應用場景
| 場景 | 說明 |
|---|---|
| 即時聊天 | LINE、Slack、Discord 的訊息即時顯示 |
| 協作編輯 | Google Docs 多人同時編輯 |
| 股票行情 | 即時更新股價變動 |
| 遊戲 | 多人連線遊戲的狀態同步 |
| 通知推播 | 網站即時通知 |
| IoT 監控 | 感測器資料即時回傳 |
WebSocket vs HTTP
| 特性 | HTTP | WebSocket |
|---|---|---|
| 連線方式 | 短連線(請求-回應) | 長連線(持續保持) |
| 通訊方向 | 單向(客戶端發起) | 雙向(雙方都可發起) |
| 延遲 | 較高 | 極低 |
| 資源消耗 | 每次請求都要建立連線 | 建立一次,持續使用 |
| 適用場景 | 一般網頁請求 | 即時互動應用 |
相關技術
- Socket.IO:封裝 WebSocket 的 JavaScript 函式庫,提供自動重連、房間等功能
- Server-Sent Events (SSE):伺服器單向推送,比 WebSocket 簡單但只能單向
- gRPC:Google 的 RPC 框架,也支援串流通訊
動手做:即時聊天室 Mini Project
看了這麼多概念,不如直接寫一個能跑的聊天室來體會 WebSocket 的威力。這個 demo 只需要 Node.js 和瀏覽器,5 分鐘就能跑起來。
Server 端(Node.js + ws)
// server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
const clients = new Set();
wss.on('connection', (ws) => {
clients.add(ws);
console.log(`新連線,目前 ${clients.size} 人在線`);
// 廣播給所有人
ws.on('message', (message) => {
const data = JSON.parse(message);
clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
user: data.user,
text: data.text,
time: new Date().toLocaleTimeString()
}));
}
});
});
ws.on('close', () => {
clients.delete(ws);
console.log(`斷線,剩餘 ${clients.size} 人在線`);
});
});
console.log('WebSocket Server 啟動在 ws://localhost:8080');Client 端(純 HTML + JS)
<!DOCTYPE html>
<html>
<body>
<div id="messages" style="height:300px;overflow-y:auto;border:1px solid #ccc;padding:10px;margin-bottom:10px;"></div>
<input id="name" placeholder="你的名字" style="width:100px" />
<input id="input" placeholder="輸入訊息..." style="width:300px" />
<button onclick="send()">送出</button>
<script>
const ws = new WebSocket('ws://localhost:8080');
const messages = document.getElementById('messages');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
messages.innerHTML += `<p><b>${data.user}</b> [${data.time}]: ${data.text}</p>`;
messages.scrollTop = messages.scrollHeight;
};
ws.onopen = () => messages.innerHTML += '<p style="color:green">✅ 已連線</p>';
ws.onclose = () => messages.innerHTML += '<p style="color:red">❌ 已斷線</p>';
function send() {
const name = document.getElementById('name').value || '匿名';
const text = document.getElementById('input').value;
if (text) {
ws.send(JSON.stringify({ user: name, text }));
document.getElementById('input').value = '';
}
}
document.getElementById('input').addEventListener('keypress', (e) => {
if (e.key === 'Enter') send();
});
</script>
</body>
</html>跑起來試試
# 1. 初始化專案
mkdir ws-chat && cd ws-chat
npm init -y
npm install ws
# 2. 建立 server.js(貼上面的 code)
# 3. 啟動 server
node server.js
# 4. 開兩個瀏覽器分頁,打開 index.html
# 5. 兩邊都能即時看到對方的訊息!這個 demo 展示了什麼
回頭看本文介紹的核心概念,這個小聊天室其實全都用上了:
- 全雙工通訊:Server 可以主動 push 訊息給所有 client
- 持久連線:連線建立後不會斷,不用每次都重新握手
- 廣播模式:一個人發訊息,所有人都收到
下一步
如果要做成正式產品,還需要:
- 心跳機制(ping/pong)防止連線 timeout
- 重連邏輯(斷線後自動重連)
- 房間機制(不是所有人都收到)
- 認證(連線時驗證 token)
這些概念在本文的其他章節都有介紹。