iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0
JavaScript

不要再說你不會後端!30 天 Node.js 降維打擊指南 系列 第 25 篇

Day 25:突破 HTTP 的限制,WebSocket 與 Socket.io 雙向通訊

  • 分享至 

  • xImage
  •  

正式進入第五階段:「即時通訊與進階應用」!

到目前為止,我們寫的所有 API 都基於 HTTP 協定。HTTP 的特性是「單向的」:前端(客戶端)發出請求,後端(伺服器)給予回應,然後連線就中斷了。

如果伺服器有新資料(例如有人傳了新訊息到聊天室),伺服器是無法主動通知前端的。前端只能不斷地發送請求去問「有新訊息嗎?」(這稱為輪詢 Polling),非常浪費資源。

為了解決這個問題,WebSocket 誕生了。它能在前後端之間建立一條「持續不斷開的雙向高速公路」,雙方隨時都可以主動丟資料給對方。


1. 業界標準:Socket.io 登場

雖然 Node.js 可以原生實作 WebSocket,但實務上我們幾乎都會使用 Socket.io 這個套件。它不僅封裝了複雜的底層連線,還自帶了「斷線重連」、「廣播(發給所有人)」、「房間(發給特定群組)」等超強功能。

安裝 Socket.io:
在專案中執行:

npm install socket.io

2. 實作:與 Express 完美結合

Socket.io 沒辦法直接掛在 Express 上,我們必須把 Express 降級(或說還原)包裝進 Node.js 內建的 http 模組中。

打開你的 app.js(或新建一個 server.js),修改成以下架構:

import express from 'express';
import http from 'http'; // 引入內建的 http 模組
import { Server } from 'socket.io'; // 引入 Socket.io

const app = express();

// 1. 將 Express 實例交給 http.createServer 包裝
const server = http.createServer(app);

// 2. 將包裝好的 server 交給 Socket.io,建立 WebSocket 伺服器
// cors 設定是為了允許跨網域連線 (前端在不同 port 也能連)
const io = new Server(server, {
    cors: { origin: "*" }
});

// 一般的 HTTP 路由依然可以正常運作
app.get('/', (req, res) => {
    res.send('<h1> WebSocket 伺服器已啟動</h1>');
});

// 3. 監聽 Socket 連線事件
// 'connection' 是內建事件,只要有前端連上線就會觸發
io.on('connection', (socket) => {
    // 每個連上來的客戶端都會有一個專屬的 socket.id
    console.log(` 新使用者連線!ID: ${socket.id}`);

    // 監聽前端發送的自訂事件 (例如 'chat_message')
    socket.on('chat_message', (msg) => {
        console.log(`收到來自 ${socket.id} 的訊息:`, msg);

        // 廣播魔法:io.emit 會把訊息發送給「所有」連線中的客戶端
        io.emit('broadcast_message', {
            sender: socket.id,
            text: msg,
            time: new Date().toLocaleTimeString()
        });
    });

    // 監聽斷線事件
    socket.on('disconnect', () => {
        console.log(` 使用者已斷線:${socket.id}`);
    });
});

// 4. 注意!這裡要改成用 `server.listen` 而不是 `app.listen`
const PORT = 3000;
server.listen(PORT, () => {
    console.log(`伺服器啟動於 http://localhost:${PORT}`);
});

3. 核心觀念:發送與接收

在 Socket.io 的世界裡,溝通就像是在「傳接球」,只有兩個最重要的動作:

  • on (接球):用來監聽特定的事件名稱。
  • emit (丟球):用來觸發特定的事件名稱,並夾帶資料。

三種常見的丟球方式 (Emit):

  1. socket.emit('event', data):只傳給當下觸發這個行為的那個前端(單聊)。
  2. io.emit('event', data):廣播給全伺服器所有連線中的前端(世界頻道)。
  3. socket.broadcast.emit('event', data):廣播給除了自己以外的所有人(例如:系統提示「XXX 已加入聊天室」)。

4. 如何測試?

測試 WebSocket 不能用普通的瀏覽器網址列,最快的方法是使用 Postman (它支援 WebSocket 連線測試),或是寫一個極簡的 HTML 前端頁面並引入 Socket.io 的 Client 端腳本來連線對話。


上一篇
Day 24:檔案上傳實戰 (Multer)
下一篇
Day 26:Socket.io 的進階玩法(Rooms 房間與 Namespace)
系列文
不要再說你不會後端!30 天 Node.js 降維打擊指南 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言