跳至主要內容

開始使用

在本指南中,我們將建立一個基本的聊天應用程式。它幾乎不需要任何 Node.JS 或 Socket.IO 的基本先備知識,因此非常適合所有知識程度的使用者。

簡介

使用流行的網頁應用程式堆疊(例如 LAMP (PHP))撰寫聊天應用程式通常非常困難。它涉及輪詢伺服器以取得變更,追蹤時間戳記,而且速度比預期慢很多。

Socket 傳統上是大多數即時聊天系統建構的解決方案,它提供用戶端與伺服器之間的雙向通訊管道。

這表示伺服器可以將訊息「推播」給用戶端。每當您撰寫聊天訊息時,其概念是伺服器會取得該訊息並將其推播給所有其他已連線的用戶端。

網頁架構

第一個目標是設定一個簡單的 HTML 網頁,用於提供表單和訊息清單。我們將使用 Node.JS 網頁架構 express 來達成此目的。請確定已安裝 Node.JS

首先,讓我們建立一個描述專案的 package.json 清單檔。我建議您將其放在專用的空目錄中(我將我的目錄命名為 chat-example)。

{
"name": "socket-chat-example",
"version": "0.0.1",
"description": "my first socket.io app",
"dependencies": {}
}
注意

「名稱」屬性必須是唯一的,您不能使用「socket.io」或「express」等值,因為 npm 會在安裝相依項時抱怨。

現在,為了使用我們需要的項目輕鬆填入 dependencies 屬性,我們將使用 npm install

npm install express@4

安裝後,我們可以建立一個 index.js 檔,用於設定我們的應用程式。

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);

app.get('/', (req, res) => {
res.send('<h1>Hello world</h1>');
});

server.listen(3000, () => {
console.log('listening on *:3000');
});

這表示

  • Express 將 app 初始化為函數處理常式,您可以提供給 HTTP 伺服器 (如第 4 行所示)。
  • 我們定義路由處理常式 /,當我們進入網站首頁時會呼叫此處理常式。
  • 我們讓 http 伺服器在埠 3000 上監聽。

如果您執行 node index.js,您應該會看到下列內容

A console saying that the server has started listening on port 3000

如果您將瀏覽器指向 http://localhost:3000

A browser displaying a big 'Hello World'

提供 HTML

到目前為止,在 index.js 中,我們呼叫 res.send 並傳遞給它一個 HTML 字串。如果我們只將整個應用程式的 HTML 放置在那裡,我們的程式碼看起來會非常混亂,因此我們將建立一個 index.html 檔案並提供該檔案。

讓我們重構我們的路由處理常式,改用 sendFile

app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});

將下列內容放入您的 index.html 檔案

<!DOCTYPE html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }

#form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed; bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box; backdrop-filter: blur(10px); }
#input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin: 0.25rem; }
#input:focus { outline: none; }
#form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem; border-radius: 3px; outline: none; color: #fff; }

#messages { list-style-type: none; margin: 0; padding: 0; }
#messages > li { padding: 0.5rem 1rem; }
#messages > li:nth-child(odd) { background: #efefef; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form id="form" action="">
<input id="input" autocomplete="off" /><button>Send</button>
</form>
</body>
</html>

如果您重新啟動程序 (按 Control+C 並再次執行 node index.js) 並重新整理頁面,它應該看起來像這樣

A browser displaying an input and a 'Send' button

整合 Socket.IO

Socket.IO 由兩部分組成

  • 與 Node.JS HTTP 伺服器整合 (或安裝在該伺服器上) 的伺服器 socket.io
  • 載入瀏覽器端的用戶端程式庫 socket.io-client

在開發期間,socket.io 會自動為我們提供用戶端,正如我們將看到的,因此現在我們只需要安裝一個模組

npm install socket.io

這將安裝模組並將相依關係新增至 package.json。現在讓我們編輯 index.js 以新增它

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');
});

server.listen(3000, () => {
console.log('listening on *:3000');
});

請注意,我透過傳遞 server (HTTP 伺服器) 物件初始化 socket.io 的新執行個體。然後我監聽 connection 事件以接收傳入的 socket 並將其記錄到主控台。

現在在 index.html 中,在 </body> (結束 body 標籤) 之前新增下列程式碼片段

<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
</script>

這只需要載入 socket.io-client,它會公開一個 io 全域變數 (和端點 GET /socket.io/socket.io.js),然後連線。

如果您想使用用戶端端 JS 檔案的本機版本,您可以在 node_modules/socket.io/client-dist/socket.io.js 找到它。

提示

您也可以使用 CDN,而不是本機檔案 (例如 <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>)。

請注意,當我呼叫 io() 時,我沒有指定任何 URL,因為它預設會嘗試連線到提供頁面的主機。

註解

如果您在 apache 或 nginx 等反向代理之後,請參閱其文件

如果您將應用程式主機在不是網站根目錄的資料夾中(例如,https://example.com/chatapp),則您還需要在伺服器和客戶端中指定路徑

如果您現在重新啟動程序(按 Control+C 並再次執行 node index.js),然後重新整理網頁,您應該會看到主控台列印「使用者已連線」。

嘗試開啟多個分頁,您將會看到多則訊息。

A console displaying several messages, indicating that some users have connected

每個 Socket 也會觸發一個特殊的 disconnect 事件

io.on('connection', (socket) => {
console.log('a user connected');
socket.on('disconnect', () => {
console.log('user disconnected');
});
});

然後,如果您重新整理分頁多次,您可以在動作中看到它。

A console displaying several messages, indicating that some users have connected and disconnected

發射事件

Socket.IO 背後的主要概念是,您可以傳送和接收任何您想要的事件,以及任何您想要的資料。任何可以編碼為 JSON 的物件都可以,而且二進位資料也受支援。

讓我們這樣做,當使用者輸入訊息時,伺服器會將它作為 chat message 事件接收。index.html 中的 script 區段現在應該如下所示

<script src="/socket.io/socket.io.js"></script>
<script>
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('chat message', input.value);
input.value = '';
}
});
</script>

而在 index.js 中,我們列印出 chat message 事件

io.on('connection', (socket) => {
socket.on('chat message', (msg) => {
console.log('message: ' + msg);
});
});

結果應該像以下影片

廣播

我們的下一個目標是從伺服器發射事件到其他使用者。

為了將事件傳送給所有人,Socket.IO 提供了 io.emit() 方法。

io.emit('some event', { someProperty: 'some value', otherProperty: 'other value' }); // This will emit the event to all connected sockets

如果您想要將訊息傳送給所有人,除了某個發射 Socket,我們有 broadcast 旗標,可以從該 Socket 發射

io.on('connection', (socket) => {
socket.broadcast.emit('hi');
});

在這種情況下,為了簡單起見,我們會將訊息傳送給所有人,包括寄件者。

io.on('connection', (socket) => {
socket.on('chat message', (msg) => {
io.emit('chat message', msg);
});
});

而在用戶端,當我們擷取 聊天訊息 事件時,我們會將其包含在頁面中。總計用戶端 JavaScript 程式碼現在等於

<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();

var messages = document.getElementById('messages');
var form = document.getElementById('form');
var input = document.getElementById('input');

form.addEventListener('submit', function(e) {
e.preventDefault();
if (input.value) {
socket.emit('chat message', input.value);
input.value = '';
}
});

socket.on('chat message', function(msg) {
var item = document.createElement('li');
item.textContent = msg;
messages.appendChild(item);
window.scrollTo(0, document.body.scrollHeight);
});
</script>

這樣就完成了我們的聊天應用程式,大約 20 行程式碼!它看起來像這樣

作業

以下是改善應用程式的部分構想

  • 當有人連線或斷線時,廣播訊息給已連線的使用者。
  • 加入對暱稱的支援。
  • 不要將相同的訊息傳送給傳送訊息的使用者。相反地,在他們按下 Enter 鍵後,直接附加訊息。
  • 加入「{使用者} 正在輸入」功能。
  • 顯示誰在線上。
  • 加入私人訊息傳送。
  • 分享你的改善成果!

取得此範例

你可以在 GitHub 在此處找到它。

git clone https://github.com/socketio/chat-example.git