跳至主要内容

入门

在本指南中,我们将创建一个基本的聊天应用程序。它几乎不需要任何关于 Node.JS 或 Socket.IO 的基础知识,因此非常适合所有知识水平的用户。

介绍

使用流行的 Web 应用程序堆栈(如 LAMP (PHP))编写聊天应用程序通常非常困难。它涉及轮询服务器以获取更改,跟踪时间戳,并且速度比应有的速度慢得多。

传统上,套接字一直是大多数实时聊天系统构建的解决方案,它在客户端和服务器之间提供双向通信通道。

这意味着服务器可以推送消息到客户端。每当您编写聊天消息时,想法是服务器会获取它并将其推送到所有其他连接的客户端。

Web 框架

第一个目标是设置一个简单的 HTML 网页,它提供一个表单和一个消息列表。为此,我们将使用 Node.JS Web 框架express。确保Node.JS已安装。

首先,让我们创建一个package.json清单文件,它描述了我们的项目。我建议您将其放在一个专用的空目录中(我将我的目录命名为chat-example)。

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

"name" 属性必须是唯一的,您不能使用诸如 "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 服务器集成的服务器(或安装在 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 事件以获取传入的套接字并将其记录到控制台。

现在,在 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

每个套接字还会触发一个特殊的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

如果您想向除特定发射套接字以外的所有人发送消息,我们可以使用该套接字发射的broadcast 标志

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

在这种情况下,为了简单起见,我们将向所有人发送消息,包括发送者。

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

在客户端,当我们捕获chat message 事件时,我们将将其包含在页面中。全部客户端 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 行代码!它看起来像这样

作业

以下是一些改进应用程序的想法

  • 在有人连接或断开连接时向连接的用户广播消息。
  • 添加对昵称的支持。
  • 不要将相同的消息发送给发送它的用户。相反,在他们按下回车键后立即直接追加消息。
  • 添加“{user} 正在输入”功能。
  • 显示谁在线。
  • 添加私聊功能。
  • 分享您的改进!

获取此示例

您可以在 GitHub 上找到它这里

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