Socket.IO 实现原理(一篇文章让你彻底弄懂即时聊天技术)

技术讨论群【522121825】

前言

        虽然写了好几篇关于 vue-socket.io 的文章,但是也还是对底层实现原理模糊不清,甚至对Socket.io、Vue-socket.io、socket.io-client 的关系理不清楚。今天,重新阅读源码,研读官网说明,一字一句解析Socket.io的底层实现原理,让你彻底弄懂如何实现即时聊天,技术之间的关系。

vue-socket.io

        先说说这个我们最熟悉的技术吧(这个没有官网哈,我们一直看到的是socket.io 的官网)。

GitHub - MetinSeylan/Vue-Socket.io: 😻 Socket.io implementation for Vuejs and Vuex😻 Socket.io implementation for Vuejs and Vuex. Contribute to MetinSeylan/Vue-Socket.io development by creating an account on GitHub.icon-default.png?t=N7T8https://github.com/MetinSeylan/Vue-Socket.io

        Vue-Socket.io是Vuejs的Socket.io集成,易于使用,支持Vuex和组件级套接字管理。说白了,就是基于Socket.io的Vue版本,底层实现了 vuex 的使用。

        在vue-socket.io源码中,可以清晰看到,第一行,便是引入了 socket.io-client。现在你不知道 socket.io-client 没关系,继续往下看。

         我们 new 的是VueSocketIO,传入的配置就支持 vuex,这就是Vue-Socket.io。

 Socket.io

Socket.IOicon-default.png?t=N7T8https://socket.io/zh-CN/

         这个才是我们学习的重点,所有技术的核心,底层实现都源于 Socket.io。接下来,我们将重点介绍socket.io、socket.io-client、还有服务端,究竟是怎么实现的。

        Socket.IO 是一个库,可以在客户端和服务器之间实现 低延迟双向 和 基于事件的 通信,支持的服务端语言很多,Node、Java、Python、Golang,很多同学问能不能在Java上使用,是可以的哦。

         很多人都将Socket.io 与Websocket 进行混淆,官网中页描述了两者的关系,因此,socket.io  是不能直接连接 websocket 服务的。

 Engine.IO

        Engine.IO 负责建立服务器和客户端之间的低级连接,负责连接的建立、传输、短线重连等机制。

 Socket.io

        Socket.IO 通过 Engine.IO 连接提供了一些附加功能,这是实现通信的核心,通过提供的API实现响应功能,包括数据缓冲、广播、房间、命名空间等。这是第一次见到 socket.io-client 库。

内存占用与性能优化 

        socket.io 提供了完整的垃圾回收机制,并对服务器消耗做了分析,如果对服务端并发要求高的,一定仔细阅读相关章节

内存占用 | Socket.IOSocket.IO服务器消耗的资源主要取决于:icon-default.png?t=N7T8https://socket.io/zh-CN/docs/v4/memory-usage/

性能优化 | Socket.IO以下是一些提高 Socket.IO 服务器性能的技巧:icon-default.png?t=N7T8https://socket.io/zh-CN/docs/v4/performance-tuning/

socket.io-client

        至此,我们应该已经非常清晰知道,socket.io-client 其实就是Socket.io 的客户端核心库

// the following forms are similar
const socket = io("https://server-domain.com");
const socket = io("wss://server-domain.com");
const socket = io("server-domain.com"); // only in the browser when the page is served over https (will not work in Node.js)

        如果客户端与服务端不同源,则需要开启服务端 跨域资源共享 CORS

         也是我们常见的 Node服务器处理 socket.io 跨域

import { createServer } from "http";
import { Server } from "socket.io";

const httpServer = createServer();
const io = new Server(httpServer, {
  cors: {
    // 如果需要支持多源,则配置成: origin:['','']
    origin: "https://example.com",
  }
});

        客户端 Socket.io-client 基本上介绍完成了

Socket.io 服务端

        socket.io 支持我们应用不同的方式搭建服务端应用,

        

        好多人好奇,理论上,我们连接的应该是 ws / wss 的地址呀,写http多少有误解,那么,能不能实现呢?下面是最简单的 socket.io 原生的服务端,监听了 5000 端口,你能说这是 http 的嘛?还是websocket的?

const { Server } = require("socket.io");

const io = new Server({
  allowEIO3: true,
  cors: {
    origin: ["http://localhost:3000", "http://127.0.0.1:3000"],
    methods: ["GET", "POST"],
    credentials: true,
  },
});

io.on("connection", (socket) => {
  // ...
  console.log("用户连接");
});

io.listen(5000);
let httpsocket = io("http://localhost:5000");
let wssocket = io("ws://localhost:5000");

        实际上,两个都是能用的,不管是 http、express还是原生,源码中,为我们兼容了不同的方式:

        官网中,描述了Socket.io 的底层实现原理,http长轮询、websocket、webtransport,如果无法建立 WebSocket 连接,连接将回退到 HTTP 长轮询。如果自己底层都不支持 http ws地址连接,又何来的回退http长轮询呢?

         服务端中间件,这里不详细说了,跟Express的中间件类似,可以在 io 执行 connect 之前,进行数据处理:

io.use((socket, next) => {
  next();
});

io.use((socket, next) => {
  next(new Error("thou shall not pass"));
});

io.use((socket, next) => {
  // not executed, since the previous middleware has returned an error
  next();
});

Vue-socket.io与Socket.io-client的关系

        通过上面的分析,应该更加清晰看出两者的关系了吧,vue-socket.io  是socket.io 的集成封装,而socket.io-client 是socket.io 的客户端核心库。在实际开发中如何选择?如果你需要vuex的事件监听,或者想更简单的使用,推荐使用vue-socket.io,但是也有些弊端,正如我的vue3连接文章中,我们为什么要使用 socket.io 进行事件通信?

        我们使用Vue-socket.io 与 socket.io-client 进行通信,对比一下两个对象:

 socket.io-client  对象:

         其中不难看出,Vue-socket.io 的对象 是emitter、io、listener构成,而emitter是拓展的Vuex

         真正用于实现通信的是io,因此,我们在vue2 中,使用 this.$socket.emit() 发送消息外,还能用 socket.io.emit() 进行消息通信,这也就是vuex3中得出并实现的底层原理

Vue3中Socket.io-client 的使用

        通过上的对比分析,在vue3中,使用 socket.io-client 应该是最适合的,下面实现:

// 新建 socket.js 文件
import { io } from "socket.io-client";

class Socket {
  constructor() {
    this.connected = false;

    this.eventHandle();

    const socket = io("ws://localhost:5000");

    this.socket = socket;

    socket.on("connect", () => {
      this.connected = true;
    });

    socket.on("disconnect", () => {
      this.connected = false;
    });

    // 使用事件监听器的形式,才能保证每次监听到 welcome 事件,都会触发回调
    socket.on("welcome", (data) => this.emit("welcome", data));
  }

  login(data) {
    // 可以对 socket.io 连接状态进行判断、重连等操作
    this.socket.emit("login", data);
  }

  eventHandle() {
    this.obj = {};
    this.on = function (name, fn) {
      if (!this.obj[name]) {
        this.obj[name] = [];
      }
      this.obj[name].push(fn);
    };
    this.emit = function (name, val) {
      if (this.obj[name]) {
        this.obj[name].map((fn) => {
          fn(val);
        });
      }
    };
    this.off = function (name, val) {
      if (this.obj[name]) {
        if (fn) {
          let index = this.obj[name].indexOf(fn);
          if (index > -1) {
            this.obj[name].splice(index, 1);
          }
        } else {
          this.obj[name].length = 0;
          //设长度为0比obj[name] = []更优,因为如果是空数组则又开辟了一个新空间,设长度为0则不必开辟新空间
        }
      }
    };
  }
}

export const client = new Socket();

         socket.io 原生Node 实现:

// socket.io 原生搭建
const { Server } = require("socket.io");

const io = new Server({
  allowEIO3: true,
  cors: {
    origin: ["http://localhost:3000", "http://127.0.0.1:3000"],
    methods: ["GET", "POST"],
    credentials: true,
  },
});

io.on("connection", (conn) => {
  // ...
  console.log("用户连接");

  conn.on("login", (data) => {
    console.log("login", data);

    setTimeout(() => conn.emit("welcome", "服务端发送消息"), 2000);
  });
});

io.listen(5000);
// main.js
import { createApp } from "vue";
import App from "./App.vue";
import "./socket";
createApp(App).mount("#app");
// app.vue

<template>
  <div>app</div>
</template>

<script setup>
import { client } from "./socket";

client.login({ id: 11 });

client.on("welcome", (data) => {
  console.log("object", data);
});
</script>


        对比我们的这篇文章,socket.io-client在vue3中的使用,简直太简单了呀

Vue 使用 Vue-socket.io 实现即时聊天应用(Vue3连接原理分析)-CSDN博客文章浏览阅读7.4k次,点赞17次,收藏46次。总的来看,要深入了解socket的源码,知道其实现方式,基于源码,将vue3的特性结合进去_vue-socket.iohttps://blog.csdn.net/weixin_47746452/article/details/126827806?spm=1001.2014.3001.5501        结合pinia或者 vuex 的场景,应该在socket.js 中,直接进行 pinia通信就行了,也能实现类似效果,只不过不是 vuex 原生监听事件实现。

总结

        1. 我们深入了解了 Socket.io 的底层实现原理;

        2. 再次探索 Vue-Socket.io 的底层依赖;

        3. 清晰认识到 Socket.io-client 的关系;

        4. 再次构建了 Vue3 版本的socket监听实现。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是使用 Node.js 和 Socket.io 实现多人聊天室的基本步骤: 1. 安装 Node.js 和 Socket.io 首先需要安装 Node.js 和 Socket.io。可以在 Node.js 官网下载并安装 Node.js,然后在命令行中使用以下命令安装 Socket.io: ``` npm install socket.io ``` 2. 创建服务器 创建一个 Node.js 服务器,并在其中引入 Socket.io 模块。创建一个 HTTP 服务器,并使用 Socket.io 将其升级为 WebSocket 服务器。 ```javascript const http = require('http'); const express = require('express'); const socketIO = require('socket.io'); const app = express(); const server = http.createServer(app); const io = socketIO(server); server.listen(3000, () => { console.log('Server listening on port 3000'); }); ``` 3. 监听连接事件 使用 Socket.io 监听 `connection` 事件,当有新的客户端连接到服务器时,触发该事件。 ```javascript io.on('connection', (socket) => { console.log('New client connected'); }); ``` 4. 处理消息事件 在连接事件中,可以监听 `message` 事件,以接收客户端发送的消息。当接收到消息时,将其广播给所有连接到服务器的客户端。 ```javascript io.on('connection', (socket) => { console.log('New client connected'); socket.on('message', (message) => { console.log('Received message: ', message); io.emit('message', message); }); }); ``` 5. 创建客户端 创建一个 HTML 页面作为客户端,使用 Socket.io 连接到服务器,并监听 `message` 事件以接收其他客户端发送的消息。 ```html <!DOCTYPE html> <html> <head> <title>Chat Room</title> <script src="/socket.io/socket.io.js"></script> <script> const socket = io(); socket.on('message', (message) => { const li = document.createElement('li'); li.innerText = message; document.getElementById('messages').appendChild(li); }); function sendMessage() { const input = document.getElementById('messageInput'); socket.emit('message', input.value); input.value = ''; } </script> </head> <body> <ul id="messages"></ul> <input type="text" id="messageInput"> <button onclick="sendMessage()">Send</button> </body> </html> ``` 6. 运行程序 运行 Node.js 服务器,并在浏览器中打开客户端页面。多个客户端连接到服务器后,可以在聊天室中发送消息。 这就是使用 Node.js 和 Socket.io 实现多人聊天室的基本步骤。当然,还可以添加许多其他功能,例如用户身份验证、私人消息等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值