使用 Express.js 实现基于 WebSocket 的网页聊天室

前言

现如今,网页聊天室成为了人们日常生活中不可或缺的一部分。其中,WebSocket 作为实时通信的核心技术已经在各种场景中得到了广泛应用。本文将介绍使用 Express.js 实现基于 WebSocket 的网页聊天室的具体步骤,旨在帮助读者深入理解 WebSocket 技术并掌握使用 Express.js 开发实时通信应用的技巧。

环境搭建

在开始实现之前,我们需要安装并配置必要的环境:

  • Node.js:是一个基于 Chrome V8 引擎的 JavaScript 运行环境。同时也是 Express.js 的运行环境。
  • Express.js:作为 Node.js 的一个 Web 框架,它可以快速地搭建 Web 应用程序,并提供一些方便的工具、站点模板、路由等。
  • Socket.IO:是一个实时应用程序的 JavaScript 框架。

我们可以使用 Node.js 的包管理器(npm)来安装这些依赖:

npm install express socket.io --save

实现流程

现在,我们开始正式实现基于 WebSocket 的网页聊天室。

创建 Express.js 应用

首先,我们创建一个 Express.js 应用程序,作为网页聊天室的服务器:

const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);

server.listen(3000, () => {
  console.log('Server is listening on port 3000');
});

代码中,我们使用了 Express.js 的 app 对象来创建应用程序。server 对象则是一个 HTTP 服务器,它可以作为 WebSocket 的服务器端。同时,我们也初始化了 Socket.IO,使它与这个 HTTP 服务器绑定。

这里创建的服务器是一个空白的应用程序,它并没有实现任何实时通信的功能,接下来我们将通过添加 WebSocket 来实现。

添加 WebSocket

为了实现 WebSocket 的功能,我们可以使用 Socket.IO 库提供的 on 方法来监听客户端的连接请求:

io.on('connection', (socket) => {
  console.log('A user has connected.');

  // TODO: Add more code after this line.
});

在这个回调函数中,我们可以通过 socket 对象与客户端进行通信,比如发送数据、接收数据等。

在客户端连接成功后,我们需要创建一个聊天室,并将它发送给客户端。在聊天室中,可以有多个用户讨论。我们可以定义一个 JavaScript 对象来表示聊天室:

const chatroom = {
  id: Math.round(Math.random() * 1000000),
  users: [],
  messages: []
};

在这个对象中,id 属性是聊天室的唯一标识,它采用随机数的形式生成。users 属性是一个数组,用于存储当前聊天室中的用户。messages 属性则是一个数组,用于存储聊天室中的消息。

在创建好聊天室对象后,将其发送给客户端:

io.emit('chatroom-created', chatroom);

这里使用了 emit 方法来发送消息。chatroom-created 是一个事件类型,它表示创建了聊天室。在客户端中,我们可以监听这个事件,以获得聊天室信息。

处理客户端的消息

当客户端连接成功后,我们需要处理客户端发送的消息。在 Socket.IO 中,我们可以使用 on 方法来监听客户端发送的消息:

socket.on('message', (message) => {
  console.log(`User ${socket.id} sent message: ${message}`);

  // TODO: Add more code after this line.
});

在这个回调函数中,我们可以处理客户端发送的各种消息。例如,可以将用户发送的消息保存到聊天室对象中。

chatroom.messages.push({
  userId: socket.id,
  message: message
});

io.emit('message-received', {
  chatroomId: chatroom.id,
  userId: socket.id,
  message: message
});

这里,我们使用 push 方法将消息保存到聊天室对象中。然后,我们使用 emit 方法将消息发送给所有的客户端。message-received 事件是一个自定义事件,它表示有新的消息接收到。

在客户端中,我们可以监听此事件,以显示新的消息。

socket.on('message-received', (data) => {
  const message = document.createElement('p');
  message.textContent = `${data.userId}: ${data.message}`;
  document.getElementById('messages').appendChild(message);
});

这里,我们使用了 createElement 方法来创建一个新的消息元素,并将其添加到页面中。textContent 属性是一个纯文本内容,用于显示消息内容。

处理客户端的加入请求

我们还需要处理客户端加入聊天室的请求。在 Socket.IO 中,我们可以使用 on 方法监听客户端发送的 join 事件:

socket.on('join', (data) => {
  console.log(`User ${socket.id} joins chatroom ${data.chatroomId}`);

  const chatroomId = data.chatroomId;
  if (chatroomId) {
    const chatroom = chatrooms.find(c => c.id === chatroomId);
    if (chatroom) {
      chatroom.users.push(socket.id);

      socket.join(`chatroom_${chatroomId}`, () => {
        io.to(`chatroom_${chatroomId}`).emit('user-joined', {
          userId: socket.id,
          chatroomId: chatroomId
        });
      });
    }
  }
});

在这个回调函数中,我们可以从 data 参数中获取要加入的聊天室的 ID。如果聊天室存在,我们将当前用户加入到聊天室中。然后,我们使用 join 方法将当前用户加入到聊天室的房间中。最后,我们使用 emit 方法向聊天室中的其他用户发送一个通知,告诉他们当前用户已经加入了聊天室。

在客户端中,我们可以监听此事件,并将用户加入到聊天室中。

document.getElementById('join-button').addEventListener('click', () => {
  const chatroomId = document.getElementById('chatroom-id-input').value;

  socket.emit('join', {
    chatroomId: chatroomId
  });
});

在这里,我们使用 emit 方法发送一个 join 事件,并将要加入的聊天室 ID 作为参数传递。在服务器端,我们可以根据这个 ID 来判断要加入的聊天室是否存在。

处理客户端的离开请求

我们还需要处理客户端离开聊天室的请求。在 Socket.IO 中,我们可以使用 on 方法监听客户端发送的 leave 事件:

socket.on('leave', (data) => {
  console.log(`User ${socket.id} leaves chatroom ${data.chatroomId}`);

  const chatroomId = data.chatroomId;
  if (chatroomId) {
    const chatroom = chatrooms.find(c => c.id === chatroomId);
    if (chatroom) {
      chatroom.users = chatroom.users.filter(u => u !== socket.id);

      socket.leave(`chatroom_${chatroomId}`, () => {
        io.to(`chatroom_${chatroomId}`).emit('user-left', {
          userId: socket.id,
          chatroomId: chatroomId
        });
      });
    }
  }
});

在这个回调函数中,我们可以从 data 参数中获取要离开的聊天室的 ID。如果聊天室存在,我们将当前用户从聊天室中移除。然后,我们使用 leave 方法将当前用户从聊天室的房间中移除。最后,我们使用 emit 方法向聊天室中的其他用户发送一个通知,告诉他们当前用户已经离开了聊天室。

在客户端中,我们可以监听此事件,并将用户从聊天室中移除。

document.getElementById('leave-button').addEventListener('click', () => {
  const chatroomId = document.getElementById('chatroom-id-input').value;

  socket.emit('leave', {
    chatroomId: chatroomId
  });
});

在这里,我们使用 emit 方法发送一个 leave 事件,并将要离开的聊天室 ID 作为参数传递。在服务器端,我们可以根据这个 ID 来判断要离开的聊天室是否存在。

总结

通过本文的介绍,我们可以了解到使用 Express.js 实现基于 WebSocket 的网页聊天室的具体步骤。在实现过程中,我们不仅学习了 Socket.IO 的使用方法,还学习了如何使用 Express.js 创建 Web 应用程序。同时,我们还学习了如何使用 JavaScript 处理实时通信相关的事件。相信通过本文的学习,读者可以更加深入理解 WebSocket 技术,并掌握使用 Express.js 开发实时通信应用的技巧。

示例代码

完整的实现源代码如下:

const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);

const chatrooms = [];

app.use(express.static('public'));

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

io.on('connection', (socket) => {
  console.log('A user has connected.');

  socket.on('message', (message) => {
    console.log(`User ${socket.id} sent message: ${message}`);

    const chatroom = chatrooms.find(c => c.id === message.chatroomId);
    if (chatroom) {
      chatroom.messages.push({
        userId: socket.id,
        message: message.text
      });
      io.to(`chatroom_${chatroom.id}`).emit('message-received', {
        chatroomId: chatroom.id,
        userId: socket.id,
        message: message.text
      });
    }
  });

  socket.on('join', (data) => {
    console.log(`User ${socket.id} joins chatroom ${data.chatroomId}`);

    const chatroomId = data.chatroomId;
    if (chatroomId) {
      let chatroom = chatrooms.find(c => c.id === chatroomId);
      if (!chatroom) {
        chatroom = {
          id: chatroomId,
          users: [],
          messages: []
        };
        chatrooms.push(chatroom);
      }
      chatroom.users.push(socket.id);

      socket.join(`chatroom_${chatroomId}`, () => {
        io.to(`chatroom_${chatroomId}`).emit('user-joined', {
          userId: socket.id,
          chatroomId: chatroomId
        });
      });
    }
  });

  socket.on('leave', (data) => {
    console.log(`User ${socket.id} leaves chatroom ${data.chatroomId}`);

    const chatroomId = data.chatroomId;
    if (chatroomId) {
      const chatroom = chatrooms.find(c => c.id === chatroomId);
      if (chatroom) {
        chatroom.users = chatroom.users.filter(u => u !== socket.id);

        socket.leave(`chatroom_${chatroomId}`, () => {
          io.to(`chatroom_${chatroomId}`).emit('user-left', {
            userId: socket.id,
            chatroomId: chatroomId
          });
        });
      }
    }
  });

  socket.on('disconnect', () => {
    console.log(`User ${socket.id} has disconnected.`);

    const chatroom = chatrooms.find(c => c.users.includes(socket.id));
    if (chatroom) {
      chatroom.users = chatroom.users.filter(u => u !== socket.id);

      io.to(`chatroom_${chatroom.id}`).emit('user-left', {
        userId: socket.id,
        chatroomId: chatroom.id
      });
    }
  });

  io.emit('chatrooms-updated', chatrooms);
});

server.listen(3000, () => {
  console.log('Server is listening on port 3000');
});

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65968ea4eb4cecbf2da5d033


纠错反馈