node+websocket实现简单版在线聊天室(200行代码)

163 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情


websocket介绍

websocket是基于TCP的全双工通信协议,允许服务端主动向客户端推送数据,浏览器和服务器只需要完成一次握手,两者就可以直接创建持久性的链接,并进行双向数据传输。

websocket比较常见的应用场景

  1. 多人游戏,可以实时发送游戏数据。
  2. 股票交易平台。
  3. 直播平台。
  4. 定位同步。
  5. 在线聊天室。
  6. 前端常用的开发时热更新,也是基于websocket实现的。

使用node+weboscket实现简单版本的在线聊天室

  • 创建node服务并安装包(nodemon会监控代码变更时,自动重启服务器的插件)
npm i ws uuid
npm i nodemon -D
  • package的script 中添加"start": "nodemon index.js"便于调试

  • 我们先创建websocket实例

const Websocket = require("ws");
const uuid = require("uuid"); //引入创建唯一id模块
  • 启动服务器
const ws = new Websocket.Server({ port: 3000 }, () => {
  console.log("socket 服务器启动");
});
  • 这一步这里我们需要先建立一个数组,因为我们的聊天室会有多个用户,每进来一个用户,就向我们的客户端列表存储一个客户端对象,在此之前需要通过ws.on监听客户端的连接。
  • 监听到客户端进来后,我们只要掌握2个关键的方法即可,发送消息client.sendclient.on("message",() => ())监听消息, 此处的client就是我们每个连接的客户端。
// 客户端列表
const clients = [];
let clientIndex = 1;

// 建立连接
ws.on("connection", (client) => {
  // 存储客户端对象
  const id = uuid.v4();
  console.log(`client ${id} connected`);
  let nickname = `用户${clientIndex++}`;
  clients.push({
    id,
    ws: client,
    nickname,
  });

  // 每次当一个客户端连接后,我们可以广播一个消息
  sendSocketMessage();

  /*监听消息*/
  client.on("message", (message) => {
    message = JSON.parse(message);
    // 昵称修改
    if (message.indexOf("/nick") === 0) {
      const nicknameArray = message.split(" ")
      if (nicknameArray.length >= 2) {
        const nicknameMessage = `${nickname} 修改昵称为 ${nicknameArray[1]}`;
        nickname = nicknameArray[1];
        broadcastSend("nickUpdate", nicknameMessage, nickname);
      }
    } else {
      // 消息发送
      broadcastSend("message", message, nickname);
    }
  });

  /*监听断开连接*/
  client.on("close", function () {
    closeSocket();
  });

  /**
   * 关闭服务,从客户端监听列表删除
   */
  function closeSocket() {
    for (let i = 0; i < clients.length; i++) {
      if (clients[i].id == id) {
        // 发送消息
        broadcastSend("notification", "退出了", nickname);
        clients.splice(i, 1);
      }
    }
  }

  /**
   * 开启服务,发送消息
   */
  function sendSocketMessage() {
    for (let i = 0; i < clients.length; i++) {
      if (clients[i].id == id) {
        // 发送消息
        broadcastSend("notification", "来了", nickname);
      }
    }
  }
});
  • 发送消息使用频率很高,我们把发送消息单独封装一下
/**
 * 广播所有客户端消息
 * @param  {String} type     广播方式(notification, message)
 * @param  {String} message  消息
 * @param  {String} nickname 用户昵称,广播方式为admin时可以不存在
 */
function broadcastSend(type, message, nickname) {
  // 遍历所有的客户端,如果连接中
  clients.forEach((v) => {
    if (v.ws.readyState === v.ws.OPEN) {
      v.ws.send(
        JSON.stringify({
          type: type,
          nickname: nickname,
          message: message,
        })
      );
    }
  });
}

  • 然后我们开始处理html部分,用户通过html访问
<!DOCTYPE html>
<html>
  <head>
    <title>websocket在线聊天室</title>
  </head>
  <body>
    <h3>websocket在线聊天室</h3>
    <div>
      nick: <input id="user" placeholder="请输入昵称" />
      <button onclick="nickUpdateClick()">修改昵称</button>
    </div>
    <div style="display: flex; margin-top: 10px">
      <textarea
        id="sendText"
        style="margin-right: 6px"
        placeholder="请输入消息"
      ></textarea>
      <button onclick="sendMessageClick()">发送消息</button>
    </div>
    <div>
      <ul id="ul"></ul>
    </div>
  • script直接放入html中就可以了,这里主要是三个步骤,创建实例、监听消息、发送消息。
  • 当我们访问页面时,会创建一个实例,一方面当服务器发送数据后,我们可以通过ws.onmessage接受到数据,另外一方面当我们想修改名称、发送消息的话直接通过ws.send进行发送即可。
    <script>
      const ws = new WebSocket("ws://localhost:3000/"); // 监听地址端口号
      // 建立连接后
      ws.onopen = function () {
        console.log("服务器连接");
      };

      // 服务器发送数据后
      ws.onmessage = ({ data }) => {
        const resData = JSON.parse(data);
        console.log("服务端发过来数据", resData);
        appendLog(resData.type, resData.nickname, resData.message);
      };

      // 服务器关闭后
      ws.onclose = () => {
        console.log("服务器关闭");
        appendLog("close");
      };

      // 昵称修改
      function nickUpdateClick() {
        if (ws.readyState === WebSocket.OPEN) {
          ws.send(
            JSON.stringify("/nick " + document.getElementById("user").value)
          );
        }
      }

      // 发送消息
      function sendMessageClick() {
        const msg = document.getElementById("sendText");
        if (ws.readyState === WebSocket.OPEN) {
          ws.send(JSON.stringify(msg.value));
        }
        msg.value = "";
        msg.focus();
      }

      // 进行消息的显示,这里很简单没什么说的
      function appendLog(type, nickname = "", message) {
        const lessTime = new Date();
        const date = `${lessTime.getFullYear()}-${
          lessTime.getMonth() + 1
        }-${lessTime.getDate()} ${lessTime.getHours()}:${lessTime.getMinutes()}:${lessTime.getSeconds()}`;

        const messages = document.getElementById("ul");
        const messageElem = document.createElement("li");
        let messageText;
        if (type === "notification") {
          // 来了或者退出了
          messageText = `<p style="color: green">&nbsp;&nbsp;${nickname}&nbsp;&nbsp;${message}</p>`;
        } else if (type === "nickUpdate") {
          // 修改名称
          messageText = `<p style="color: orange">&nbsp;&nbsp;${message}</p>`;
        } else if (type === "message") {
          // 消息发送
          messageText = `<p style="color: blue">&nbsp;&nbsp;${nickname}说:&nbsp;&nbsp;${message}</p>`;
        } else if (type === "close") {
          // 服务器关闭
          messageText = `<p style="color: red">&nbsp;&nbsp;服务器关闭</p>`;
        }

        messageElem.innerHTML = `${date}<br/>${messageText}`;
        messages.appendChild(messageElem);
      }
    </script>

结语

日常中几乎天天都使用到了websocket,经常听说过这个东西但是没有实践过的小伙伴可以试起来了,是不是比想象的简单多了呢~