webscoket
服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
前端基础用法:
连接:
const ws = new Wenscoket('ws://xxxx')
这样就就会创建ws实例并且客户端就会与服务器进行连接。
ws.onopen=()=>{} //连接成功之后的监听
ws.onclose=()=>{} //连接失败或者关闭的回调
ws.onmessage=(data)=>{} //监听后端发送的信息
ws.onerror = ()=>{} //出错之后的回调。
ws.send(data)//向后端发送信息。
后端基本用法(node)
const WebSocket = require("ws");
const path = require("path");
const wss = new WebSocket.Server({. //新建实例
port: 9998,
});
wss.on("connection", (client) => { //开启服务
client.on("message", async (msg) => { //监听前端的发送数据,msg为前端发送回来
client.send(JSON.stringify(payload)); //向后端发送数据
//所有客户端 wss.clients,向所有连接客户端发送数据
wss.clients.forEach((item) => {
item.send(msg);
});
}
});
});
前端封装类
interface WebSocketDataParams {
socketType: string;
action: string;
chartName: string;
value: boolean;
}
interface WebSocketDataProps {
socketType: string;
action: string;
chartName: string;
value: boolean;
data: string;
}
class SocketService {
/**
* 单例 保证拿到的都是同一个实例
*/
static instance: SocketService;
static get Instance() {
if (!this.instance) {
this.instance = new SocketService();
}
return this.instance;
}
baseUrl: string;
ws: WebSocket;
callBackMapping: Record<string, any>;
isconnect: boolean;
sendRetryCount: number; //重复发送次数
connectRetryCount: number;
constructor(url: string = "ws://localhost:9998") {
this.baseUrl = url;
this.ws = {} as WebSocket;
this.callBackMapping = {};
this.isconnect = false;
this.sendRetryCount = 0;
this.connectRetryCount = 0;
}
//和服务端创建的stocket对象
//定义连接服务器的方法
connect(url = "") {
if (!window.WebSocket) {
return console.log("您的浏览器不支持WebSocket");
}
url = url ? url : this.baseUrl;
this.ws = new WebSocket(url);
//连接监听
this.ws.onopen = () => {
console.log("连接服务端成功");
this.connectRetryCount = 0;
this.isconnect = true;
};
this.ws.onclose = () => {
this.connectRetryCount++;
console.log("连接失败/关闭");
this.isconnect = false;
//当连接关闭后进行重新连接尝试
setTimeout(() => {
this.connect();
}, this.connectRetryCount * 500);
};
this.ws.onmessage = (res) => {
const recvData: WebSocketDataProps = JSON.parse(res.data);
const stocketType = recvData.socketType; //与后端约定type
//如果存在,直接调用
const callBack = this.callBackMapping[stocketType]; //执行订阅的回调
if (callBack) {
const action = recvData.action;
if (action === "getData") {
const realData = JSON.parse(recvData.data);
//将数据传给回调函数
callBack.call(this, realData);
} else if (action === "fullScreen") {
} else if (action === "themeChange") {
}
}
};
}
//注册回调函数,可能有多个scoket
registerCallBack(
socketType: string,
callBack: (data: Record<string, any>) => void
) {
this.callBackMapping[socketType] = callBack;
}
unRegisterCallBack(socketType: string) {
this.callBackMapping[socketType] = null;
}
send(data: WebSocketDataParams) {
if (this.isconnect) {
this.sendRetryCount = 0; //发送成功重置为0
this.ws.send(JSON.stringify(data));
} else {
setTimeout(() => {
this.sendRetryCount++;
this.send(data);
}, this.sendRetryCount * 500);
}
}
}
const wss = SocketService.Instance;
export { wss };
采用封装类,然后可能会有多个页面需要使用到,所以采用派发订阅的模式
监听到数据的时候拿到对应的订阅函数执行。
如,注册,然后记得注销掉。
这样一监听到后端返回来的数据就会执行回应的回调函数。