node-socket

socket.io

  • 传统的 HTTP 是一种单向请求-响应协议,客户端发送请求后,服务器才会响应并返回相应的数据。在传统的 HTTP 中,客户端需要主动发送请求才能获取服务器上的资源,而且每次请求都需要重新建立连接,这种方式在实时通信和持续获取资源的场景下效率较低。

  • Socket 提供了实时的双向通信能力,可以实时地传输数据。客户端和服务器之间的通信是即时的,数据的传输和响应几乎是实时完成的,不需要轮询或定时发送请求
    img

既 前后端实时发送消息的技术方案

实践

  1. 安装依赖
    推荐使用 socket.io
 npm install socket.io
  1. dome目录
    img

其中 package.json中增加一行 "type": "module",

  • index.js
import http from "node:http";
import { Server } from "socket.io";

const server = http.createServer();

const io = new Server(server, {
  cors: true, // 允许跨域
});

// 事件模型驱动
const groupMap = {};
io.on("connection", (socket) => {
  // 1 加入房间
  socket.on("join", (data) => {
    /**
     * name: 名字
     * room: 房间号
     */
    const { name, room } = data;
    socket.join(room); // 创建房间

    // 有这个房间
    if (groupMap[room]) {
      // 有房间直接加进去
      groupMap[room].push({ name, room, id: socket.id });
    } else {
      // 没有房间 初始化房间
      groupMap[room] = [{ name, room, id: socket.id }];
    }
    socket.emit("groupMap", groupMap); // 浏览器为维度
    socket.broadcast.emit("groupMap", groupMap); // 所有人都能看到 为了前端渲染左侧房间列表

    // 管理员消息
    socket.broadcast.to(room).emit("message", {
      name: "系统提示",
      message: `${name}加入聊天`,
    });
  });

  // 接收消息
  socket.on("message", ({ name, message, room }) => {
    // 拿到用户发的消息 发给房间里
    socket.broadcast.to(room).emit("message", { name, message });
  });
});

server.listen(3000, () => {
  console.log("listen in 3000 port");
});
  • index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }

      html,
      body,
      .room {
        height: 100%;
        width: 100%;
      }

      .room {
        display: flex;
      }

      .left {
        width: 300px;
        border-right: 0.5px solid #f5f5f5;
        background: #333;
      }

      .right {
        background: #1c1c1c;
        flex: 1;
        display: flex;
        flex-direction: column;
      }

      .header {
        background: #8d0eb0;
        color: white;
        padding: 10px;
        box-sizing: border-box;
        font-size: 20px;
      }

      .main {
        flex: 1;
        padding: 10px;
        box-sizing: border-box;
        font-size: 20px;
        overflow: auto;
      }

      .main-chat {
        color: green;
      }

      .footer {
        min-height: 200px;
        border-top: 1px solid green;
      }

      .footer .ipt {
        width: 100%;
        height: 100%;
        color: green;
        outline: none;
        font-size: 20px;
        padding: 10px;
        box-sizing: border-box;
      }

      .groupList {
        height: 100%;
        overflow: auto;
      }

      .groupList-items {
        height: 50px;
        width: 100%;
        background: #131313;
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
      }
    </style>
  </head>
  <div class="room">
    <div class="left">
      <div class="groupList"></div>
    </div>
    <div class="right">
      <header class="header">聊天室</header>
      <main class="main"></main>
      <footer class="footer">
        <div class="ipt" contenteditable></div>
      </footer>
    </div>
  </div>

  <body>
    <script src="./socket.io.js"></script>
    <script type="module">
      let name = prompt("请输入你的名字");
      let room = prompt("请输入你要进入的房间");
      const group = document.querySelector(".groupList"); // 左侧列表的父级
      const main = document.querySelector(".main"); // 消息内容的父级
      const ipt = document.querySelector(".footer .ipt"); // 输入框
      // 因为自己发的信息自己收不到 手动自己发送的信息自己添加到dom
      const addChat = ({ name, message }) => {
        const item = document.createElement("div");
        item.className = "main-chat";
        item.innerHTML = `${name}: ${message}`;
        main.appendChild(item);
      };

      const socket = io("ws://192.168.60.54:3000");
      socket.on("connect", () => {
        //   1 加入房间
        // 2 监听发消息 敲回车的事件
        document.addEventListener("keydown", (e) => {
          if (e.key === "Enter") {
            const message = ipt.innerText;
            socket.emit("message", {
              name,
              message: message,
              room,
            });
            addChat({ name, message });
            ipt.innerHTML = ``;
          }
        });
        socket.emit("join", {
          name,
          room,
        });
        // 3 拿到groupMap 与 message
        socket.on("groupMap", (data) => {
          group.innerHTML = ``;
          Object.keys(data).forEach((key) => {
            const item = document.createElement("div");
            item.className = "groupList-items";
            item.innerHTML = `房间号: ${key};房间人数:${data[key].length}`;
            group.appendChild(item);
          });
        });
        socket.on("message", (data) => {
          addChat(data);
        });
      });
    </script>
  </body>
</html>
posted @ 2024-12-12 15:11  进击的娃哈哈  阅读(3)  评论(0编辑  收藏  举报