1、前言

Socket.io将数据传输部分独立出来形成engine.io,engine.io对WebSocket和AJAX轮询进行了封装,形成了一套API,屏蔽了细节差异和兼容性问题,实现了跨浏览器/跨设备进行双向数据通信。
WebSocket是一种双向通信协议,WebSocket与HTTP协议一样都是基于TCP的

2、功能与效果

  • 登陆、登出
  • 多用户聊天
  • 效果如下

    1. ![未命名.gif](https://cdn.nlark.com/yuque/0/2020/gif/1225858/1607595569933-6c001464-170e-4935-b7f6-f9f9d1caae09.gif#align=left&display=inline&height=240&margin=%5Bobject%20Object%5D&name=%E6%9C%AA%E5%91%BD%E5%90%8D.gif&originHeight=240&originWidth=360&size=246169&status=done&style=none&width=360)

    3、目录结构

    1. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1225858/1607595802569-04ce3a9f-89fa-4dff-ac18-04227f714448.png#align=left&display=inline&height=195&margin=%5Bobject%20Object%5D&name=image.png&originHeight=390&originWidth=602&size=121333&status=done&style=none&width=301)

4、Node端服务

socket.io 服务需要依赖http服务,通过安装express,起一个http服务,然后安装socket.io

  1. npm i socket.io express -S

通过访问localhost:3000 访问页面

  1. // app.js
  2. const app = require("express")();
  3. const server = require("http").Server(app);
  4. const io = require("socket.io")(server);
  5. app.get("/", function (req, res, next) {
  6. res.sendFile(__dirname + "/index.html");
  7. });
  8. server.listen("3000", function () {
  9. console.log("服务启动在3000");
  10. });

服务端用到的socket.io的api
  1. const io = require("socket.io")(server);
  2. // 连接socket.io
  3. io.on("connect", (socket) => {
  4. // socket.emit 代表着向客户端发送消息,客户端通过 socket.on("receive", params); 接收
  5. socket.emit("receive",params);
  6. // socket.on('send') 代表着接收客户端发来的消息, 客户端通过 socket.emit("send", params); 发送
  7. socket.on("send",params);
  8. // io.emit("all") 代表广播,给每个客户端发消息
  9. io.emit("all",params)
  10. })

5、客户端socket的使用

客户端主要是使用socket.io的 emit 发送消息和on接收消息,和服务端的api相同
index.html

  1. // 客户端需要引入包
  2. <script src="/socket.io/socket.io.js"></script>
  3. <script>
  4. // 使用
  5. let socket = io("http://localhost:3000");
  6. // 1、等待服务端的消息通知
  7. socket.on("notify", (username) => {
  8. message(`${username}加入进来了!`, 1);
  9. });
  10. // 2、发送消息给服务端
  11. function send() {
  12. if (!info.value) message("请输入消息", 0);
  13. socket.emit("message", people);
  14. }
  15. </script>

6、用户登陆和列表展示流程

  • 客户端登陆通过sock.emit 将账号名称发给服务端
  • 服务端通过socket.emit 发送给客户端登陆成功, 通过 io.emit 广播给所有客户端
  • 服务端保存用户信息到user中,广播给客户端
  • 客户端通过拿到的数据展示

7、完成代码

1、客户端

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>Title</title>
  6. </head>
  7. <style>
  8. #btn,
  9. #info {
  10. display: none;
  11. }
  12. </style>
  13. <body>
  14. <h4>简易聊天室</h4>
  15. <div>
  16. 当前用户列表
  17. <ul id="userList"></ul>
  18. </div>
  19. <input type="text" id="account" />
  20. <button onclick="login()">登陆</button>
  21. <button onclick="loginOut()">退出登陆</button>
  22. <br />
  23. <input type="text" id="info" />
  24. <button id="btn" onclick="send()">发送</button>
  25. <div id="text"></div>
  26. <script src="/socket.io/socket.io.js"></script>
  27. <script>
  28. let account = document.querySelector("#account");
  29. let info = document.querySelector("#info");
  30. let text = document.querySelector("#text");
  31. let btn = document.querySelector("#btn");
  32. let userList = document.querySelector("#userList");
  33. let socket = io("http://localhost:3000");
  34. let SUCCESS = "#4d834d",
  35. ERROR = "#f00",
  36. OTHER = "orange";
  37. let people = {
  38. username: "",
  39. message: "",
  40. };
  41. // 1、 登陆成功,通知服务端,广播消息
  42. function login() {
  43. let accountName = account.value;
  44. if (!accountName) return message("请输入账号!", 0);
  45. if (people.username) return message("您已登陆!", 0);
  46. people.username = accountName;
  47. socket.emit("login", accountName);
  48. }
  49. socket.on("loginSuccess", (users) => {
  50. message(people.username + "登陆成功!", 1);
  51. info.style.display = "inline-block";
  52. btn.style.display = "inline-block";
  53. });
  54. socket.on("updateUserList", (users) => {
  55. // 更新用户列表
  56. let liTxt = "";
  57. users.map((item) => {
  58. let txt =
  59. item.username === people.username
  60. ? item.username + "(本人)"
  61. : item.username;
  62. liTxt += "<li style='color:blue;'>" + txt + "</li>";
  63. });
  64. userList.innerHTML = liTxt;
  65. });
  66. // 2、发送消息,广播消息
  67. function send() {
  68. if (!info.value) message("请输入消息", 0);
  69. people.message = info.value;
  70. info.value = "";
  71. socket.emit("message", people);
  72. }
  73. // 3、退出操作
  74. function loginOut() {
  75. socket.emit("loginOut");
  76. info.style.display = "none";
  77. btn.style.display = "none";
  78. account.value = "";
  79. people = {};
  80. }
  81. socket.on("loginOut", (username) => {
  82. if (username) {
  83. message(username + "退出了群聊!", 0);
  84. return;
  85. }
  86. message("您未登陆!", 0);
  87. });
  88. // 处理加入通知
  89. socket.on("notify", (username) => {
  90. message(`${username}加入进来了!`, 1);
  91. });
  92. // 接收处理消息
  93. socket.on("recevied", (username, msg) => {
  94. let date = new Date().toLocaleString();
  95. message(`${username}: ${msg}---------${date}`, 3);
  96. });
  97. // 统一处理消息
  98. function message(message, status) {
  99. let color = { 1: SUCCESS, 0: ERROR, 3: OTHER };
  100. text.innerHTML += `<span style='color:${color[status]};'>${message}</span><br/>`;
  101. }
  102. </script>
  103. </body>
  104. </html>

2、服务端

  1. const app = require("express")();
  2. const server = require("http").Server(app);
  3. const io = require("socket.io")(server);
  4. server.listen("3000", function () {
  5. console.log("服务启动在3000");
  6. });
  7. app.get("/", function (req, res, next) {
  8. res.sendFile(__dirname + "/index.html");
  9. });
  10. let users = []; // 记录当前所有用户信息
  11. // 连接
  12. io.on("connect", (socket) => {
  13. // 登陆处理
  14. socket.on("login", (username) => {
  15. users.push({
  16. username: username,
  17. });
  18. socket.username = username;
  19. name = username;
  20. // 推送登陆成功
  21. socket.emit("loginSuccess");
  22. //广播事件 io.emi 加入通知
  23. io.emit("notify", username);
  24. io.emit("updateUserList", users);
  25. });
  26. // 消息处理
  27. socket.on("message", (people) => {
  28. let { username, message } = people;
  29. io.emit("recevied", username, message);
  30. });
  31. // 退出连接
  32. socket.on("loginOut", () => {
  33. // 退出的时候广播,退出处理
  34. io.emit("loginOut", socket.username);
  35. users = users.filter((item) => item.username !== socket.username);
  36. socket.username = "";
  37. io.emit("updateUserList", users, socket.username);
  38. });
  39. });

5、参考

文档:https://socket.io/docs/v3/index.html

websocket+nodejs实现聊天室: https://juejin.cn/post/6844904178481889293

socketio https://www.w3cschool.cn/socket/socket-k49j2eia.html