1、幾十行程式碼實現一個聊天室

語言: CN / TW / HK

1、前言

Socket.io將資料傳輸部分獨立出來形成engine.io,engine.io對WebSocket和AJAX輪詢進行了封裝,形成了一套API,遮蔽了細節差異和相容性問題,實現了跨瀏覽器/跨裝置進行雙向資料通訊。 WebSocket是一種**雙向通訊協議,**WebSocket與HTTP協議一樣都是基於TCP的

2、功能與效果

  • 登陸、登出
  • 多使用者聊天
  • 效果如下

   未命名.gif

3、目錄結構

   image.png

4、Node端服務

socket.io 服務需要依賴http服務,通過安裝express,起一個http服務,然後安裝socket.io

npm i socket.io express -S
複製程式碼

通過訪問localhost:3000 訪問頁面

// app.js
const app = require("express")();
const server = require("http").Server(app);
const io = require("socket.io")(server);

app.get("/", function (req, res, next) {
  res.sendFile(__dirname + "/index.html");
});

server.listen("3000", function () {
  console.log("服務啟動在3000");
});
複製程式碼
服務端用到的socket.io的api
const io = require("socket.io")(server);
// 連線socket.io
io.on("connect", (socket) => {
    // socket.emit 代表著向客戶端傳送訊息,客戶端通過 socket.on("receive", params); 接收
    socket.emit("receive",params);
    // socket.on('send') 代表著接收客戶端發來的訊息, 客戶端通過 socket.emit("send", params); 傳送
    socket.on("send",params);
    // io.emit("all") 代表廣播,給每個客戶端發訊息
    io.emit("all",params)
}) 
複製程式碼

5、客戶端socket的使用

客戶端主要是使用socket.io的 emit 傳送訊息和on接收訊息,和服務端的api相同 index.html

// 客戶端需要引入包
<script src="/socket.io/socket.io.js"></script>
<script>
    // 使用
    let socket = io("http://localhost:3000");
    // 1、等待服務端的訊息通知
    socket.on("notify", (username) => {
      message(`${username}加入進來了!`, 1);
    });
     // 2、傳送訊息給服務端
    function send() {
      if (!info.value) message("請輸入訊息", 0);
      socket.emit("message", people);
    }
</script>
複製程式碼

6、使用者登陸和列表展示流程

  • 客戶端登陸通過sock.emit 將賬號名稱發給服務端
  • 服務端通過socket.emit 傳送給客戶端登陸成功, 通過 io.emit 廣播給所有客戶端
  • 服務端儲存使用者資訊到user中,廣播給客戶端
  • 客戶端通過拿到的資料展示

7、完成程式碼

1、客戶端

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <style>
    #btn,
    #info {
      display: none;
    }
  </style>
  <body>
    <h4>簡易聊天室</h4>
    <div>
      當前使用者列表
      <ul id="userList"></ul>
    </div>
    <input type="text" id="account" />
    <button onclick="login()">登陸</button>
    <button onclick="loginOut()">退出登陸</button>
    

    <input type="text" id="info" />
    <button id="btn" onclick="send()">傳送</button>
    <div id="text"></div>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      let account = document.querySelector("#account");
      let info = document.querySelector("#info");
      let text = document.querySelector("#text");
      let btn = document.querySelector("#btn");
      let userList = document.querySelector("#userList");
      let socket = io("http://localhost:3000");

      let SUCCESS = "#4d834d",
        ERROR = "#f00",
        OTHER = "orange";

      let people = {
        username: "",
        message: "",
      };

      // 1、 登陸成功,通知服務端,廣播訊息
      function login() {
        let accountName = account.value;
        if (!accountName) return message("請輸入賬號!", 0);
        if (people.username) return message("您已登陸!", 0);
        people.username = accountName;
        socket.emit("login", accountName);
      }

      socket.on("loginSuccess", (users) => {
        message(people.username + "登陸成功!", 1);
        info.style.display = "inline-block";
        btn.style.display = "inline-block";
      });

      socket.on("updateUserList", (users) => {
        // 更新使用者列表
        let liTxt = "";
        users.map((item) => {
          let txt =
            item.username === people.username
              ? item.username + "(本人)"
              : item.username;
          liTxt += "<li style='color:blue;'>" + txt + "</li>";
        });
        userList.innerHTML = liTxt;
      });

      // 2、傳送訊息,廣播訊息
      function send() {
        if (!info.value) message("請輸入訊息", 0);
        people.message = info.value;
        info.value = "";
        socket.emit("message", people);
      }

      // 3、退出操作
      function loginOut() {
        socket.emit("loginOut");
        info.style.display = "none";
        btn.style.display = "none";
        account.value = "";
        people = {};
      }

      socket.on("loginOut", (username) => {
        if (username) {
          message(username + "退出了群聊!", 0);
          return;
        }
        message("您未登陸!", 0);
      });

      // 處理加入通知
      socket.on("notify", (username) => {
        message(`${username}加入進來了!`, 1);
      });

      // 接收處理訊息
      socket.on("recevied", (username, msg) => {
        let date = new Date().toLocaleString();
        message(`${username}: ${msg}---------${date}`, 3);
      });

      // 統一處理訊息
      function message(message, status) {
        let color = { 1: SUCCESS, 0: ERROR, 3: OTHER };
        text.innerHTML += `<span style='color:${color[status]};'>${message}</span><br/>`;
      }
    </script>
  </body>
</html>

複製程式碼

2、服務端

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

server.listen("3000", function () {
  console.log("服務啟動在3000");
});

app.get("/", function (req, res, next) {
  res.sendFile(__dirname + "/index.html");
});

let users = []; // 記錄當前所有使用者資訊

// 連線
io.on("connect", (socket) => {
  // 登陸處理
  socket.on("login", (username) => {
    users.push({
      username: username,
    });
    socket.username = username;
    name = username;
    // 推送登陸成功
    socket.emit("loginSuccess");
    //廣播事件 io.emi 加入通知
    io.emit("notify", username);
    io.emit("updateUserList", users);
  });
  // 訊息處理
  socket.on("message", (people) => {
    let { username, message } = people;
    io.emit("recevied", username, message);
  });

  // 退出連線
  socket.on("loginOut", () => {
    // 退出的時候廣播,退出處理
    io.emit("loginOut", socket.username);
    users = users.filter((item) => item.username !== socket.username);
    socket.username = "";
    io.emit("updateUserList", users, socket.username);
  });
});

複製程式碼

5、參考

文件:socket.io/docs/v3/ind…
websocket+nodejs實現聊天室: juejin.cn/post/684490…