技术讨论群【522121825】
虽然写了好几篇关于 vue-socket.io 的文章,但是也还是对底层实现原理模糊不清,甚至对Socket.io、Vue-socket.io、socket.io-client 的关系理不清楚。今天,重新阅读源码,研读官网说明,一字一句解析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.https://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.IOhttps://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 负责建立服务器和客户端之间的低级连接,负责连接的建立、传输、短线重连等机制。
Socket.IO 通过 Engine.IO 连接提供了一些附加功能,这是实现通信的核心,通过提供的API实现响应功能,包括数据缓冲、广播、房间、命名空间等。这是第一次见到 socket.io-client 库。
socket.io 提供了完整的垃圾回收机制,并对服务器消耗做了分析,如果对服务端并发要求高的,一定仔细阅读相关章节!
内存占用 | Socket.IOSocket.IO服务器消耗的资源主要取决于:https://socket.io/zh-CN/docs/v4/memory-usage/
性能优化 | Socket.IO以下是一些提高 Socket.IO 服务器性能的技巧:https://socket.io/zh-CN/docs/v4/performance-tuning/
至此,我们应该已经非常清晰知道,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 支持我们应用不同的方式搭建服务端应用,
好多人好奇,理论上,我们连接的应该是 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 的集成封装,而socket.io-client 是socket.io 的客户端核心库。在实际开发中如何选择?如果你需要vuex的事件监听,或者想更简单的使用,推荐使用vue-socket.io,但是也有些弊端,正如我的vue3连接文章中,我们为什么要使用 socket.io 进行事件通信?
我们使用Vue-socket.io 与 socket.io-client 进行通信,对比一下两个对象:
其中不难看出,Vue-socket.io 的对象 是emitter、io、listener构成,而emitter是拓展的Vuex
真正用于实现通信的是io,因此,我们在vue2 中,使用 this.$socket.emit() 发送消息外,还能用 socket.io.emit() 进行消息通信,这也就是vuex3中得出并实现的底层原理。
通过上的对比分析,在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
app
对比我们的这篇文章,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监听实现。