websocket的基本使用,用于测试前端能否正常获取到后台数据
后台代码编写:
javascript
const path = require("path");
const fileUtils = require("../utils/file_utils");
const WebSocket = require("ws");
// 创建WebSocket服务端的对象,绑定的端口号是9998
const wss = new WebSocket.Server({
port: 9998,
});
// 服务端开启了监听
// 导出一个函数listen
module.exports.listen = () => {
// 对客户端的连接事件进行监听
// client: 代表的是客户端的连接socket对象
wss.on("connection", (client) => {
console.log("有客户端连接成功了...");
// 对客户端的连接对象进行message事件的监听
// msg:由客户端发给服务器的数据
client.on("message", async (msg) => {
console.log("客户端发送数据给服务器:" + msg);
let payload = JSON.parse(msg);
const action = payload.action;
if (action === "getData") {
let filePath = "../data/" + payload.chartName + ".json";
// payload.charName; // trend seller map rank hot stock
filePath = path.join(__dirname, filePath);
const ret = await fileUtils.getFileJsonData(filePath);
// 需要再服务器获取到数据的基础上,增加一个data的字段
// data对应的值,就是某个json文件的内容
payload.data = ret;
client.send(JSON.stringify(payload));
} else {
// 原封不动的将所有收到的数据转发给每一个处于连接状态的客户端
// wss.clients //所有客户端的连接
wss.clients.forEach((client) => {
client.send(JSON.stringify(payload));
});
}
// 由服务端往客户端发送数据
// client.send("hello socket from backend");
});
});
};
调用的获取文件路径的方法(getFileJsonData):
javascript
// 读取文件的工具方法
const fs = require("fs");
module.exports.getFileJsonData = (filePath) => {
// 根据文件的路径,读取文件的内容
return new Promise((resolve, reject) => {
fs.readFile(filePath, "utf-8", (error, data) => {
if (error) {
// 读取文件失败
reject(error);
} else {
// 读取文件成功
// return data; // 这里的return返回的是这个函数的调用者,而不是getFileJsonData这个函数的调用者
// 读取文件是一个异步任务,对于一个异步任务,我们也不能通过return的方式来将数据返回给调用者
resolve(data);
}
});
});
};
前端使用:
javascript
<button id="connect">连接</button>
<button id="send" disabled="true">发送数据</button>
<br />从服务端接收的数据如下: <br />
<span id="recv"></span>
<script>
var connect = document.querySelector("#connect");
var send = document.querySelector("#send");
var recv = document.querySelector("#recv");
let ws = null;
connect.onclick = function () {
ws = new WebSocket("ws://localhost:9998");
ws.onopen = () => {
console.log("连接服务端成功了...");
send.disabled = false;
};
ws.onclose = () => {
console.log("连接服务器失败或关闭");
send.disabled = true;
};
ws.onmessage = (msg) => {
console.log("接收从服务器发送过来的数据了");
console.log(msg);
recv.innerHTML = msg.data;
};
};
send.onclick = function () {
ws.send(
JSON.stringify({
action: "getData",
socketType: "trendData",
chartName: "trend",
value: "",
})
);
ws.send(
JSON.stringify({
action: "fullScreen",
socketType: "fullScreen",
chartName: "trend",
value: true | false,
})
);
ws.send(
JSON.stringify({
action: "themeScreen",
socketType: "themeScreen",
chartName: "",
value: "",
})
);
};
</script>
websocket用来改造电商平台项目思维导图:
坐标轴的方向是一个从左往右的方向,朝那个方向就选取x1,y1,x2,y2就选取那几个值
横向:0,0,1,0
竖向:0,0,0,1
报错信息:
js:31[Vue warn]:挂接钩子时出错:"InvalidStateError:在WebSocket上执行send失败:仍处于连接状态。"
代码中的注释解释:
// 有一种报错情况,在组件还没有进行连接成功之前,连接需要一点时间,这个组件就已经进行了加载,就会调用mounted当中指明的send的方法,而send方法是socket_service.js当中定义的一个方法,就会往ws来进行一个send方法的调用,而此时此刻,还没有连接成功
javascript
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
// 引入字体的文件
import "./assets/font/iconfont.css";
// 引入全局的样式文件
import "./assets/css/global.less";
import axios from "axios";
import SocketService from "@/utils/socket_service";
// 对服务端的数据进行连接
// SocketService.Instance().connect(); //这样写会报错,报错说明是Instance是不需要就加上小括号的
// 有一种报错情况,在组件还没有进行连接成功之前,连接需要一点时间,这个组件就已经进行了加载,就会调用mounted当中指明的send的方法,而send方法是socket_service.js当中定义的一个方法,就会往ws来进行一个send方法的调用,而此时此刻,还没有连接成功
SocketService.Instance.connect();
// 其他的组件调用websocket的方式 this.$socket
Vue.prototype.$socket = SocketService.Instance;
// 请求的基准路径的配置
axios.defaults.baseURL = "http://127.0.0.1:8888/api/";
// 将axios挂载到Vue的原型对象上
// 在别的组件中 this.$http
Vue.prototype.$http = axios;
// 将全局的echarts对象挂载到Vue的原型对象中
// 别的组件中this.$echarts
Vue.prototype.$echarts = window.echarts;
Vue.config.productionTip = false;
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
解决方法,添加一个连接标识,判断websocket连接成功还是失败,true连接成功,false连接失败
项目改造后的后台编写:
javascript
export default class SocketService {
// 单例
static instance = null;
static get Instance() {
if (!this.instance) {
this.instance = new SocketService();
}
return this.instance;
}
// 和服务器连接的socket对象
ws = null;
// 怎么在得到数据之后,将每个图表数据传递给每个图表组件,让图表数据可以得到更新
// 先将每一个图表组件的某一个方法先存储在我们当前的这个模块中,比如一个实例属性中,一旦得到有服务端给我们的数据之后,在调用我们之前所存储起来的那个方法,就可以将数据传递给每个图表组件
// 存储回调函数
callBackMapping = {};
// 标识是否连接成功
connected = false;
// 记录重试的次数
sendRetryCount = 0;
// 重新连接尝试的次数
connectRetryCount = 0;
// 定义连接服务器的方法
connect() {
// 连接服务器
if (!window.WebSocket) {
return console.log("您的浏览器不支持WebSocket");
}
this.ws = new WebSocket("ws://localhost:9998");
// 连接成功的事件
this.ws.onopen = () => {
console.log("连接服务器成功了");
this.connected = true;
// 重置重新连接的次数
this.connectRetryCount = 0;
};
// 1.连接服务器失败
// 2.当连接成功之后,服务器关闭的情况
this.ws.onclose = () => {
console.log("连接服务器失败");
// 标识是否连接成功,没有连接成功
this.connected = false;
this.connectRetryCount++;
// 这个时候应该进行重新连接的尝试,但是每一次连接失败,下一次的连接都是500毫秒 不太合适,因为尝试的越多,可能服务器已经坏了,或者已经关闭了,就没有必要那么频繁的次数来进行重新连接的尝试
setTimeout(() => {
this.connect();
}, 500 * this.connectRetryCount);
};
this.ws.onmessage = (msg) => {
console.log("从服务器获取了数据");
// 真正服务端发送过来的原始数据时在msg中的data字段
// 如果在的得到数据之后把数据传递给每个图表组件会好一些,因为只有图表组件需要数据
console.log(msg.data, msg, "--\\\\");
const recvData = JSON.parse(msg.data);
const socketType = recvData.socketType;
// 判断回调函数是否存在
if (this.callBackMapping[socketType]) {
const action = recvData.action;
if (action === "getData") {
const realData = JSON.parse(recvData.data);
this.callBackMapping[socketType].call(this, realData);
} else if (action === "fullScreen") {
this.callBackMapping[socketType].call(this, recvData);
} else if (action === "themeScreen") {
this.callBackMapping[socketType].call(this, recvData);
}
}
};
}
// 下面的这个三个方法需要通过组件来调用
// 回调函数的注册
// socketType 这个函数的唯一标识,callBack这个函数的回调函数
registerCallBack(socketType, callBack) {
this.callBackMapping[socketType] = callBack;
}
// 取消某一个回调函数
// socketType 这个函数的唯一标识
unRegisterCallBack(socketType) {
this.callBackMapping[socketType] = null;
}
// 发送数据的方法
send(data) {
// 判断此时此刻有没有连接成功
if (this.connected) {
this.sendRetryCount = 0;
this.ws.send(JSON.stringify(data));
} else {
this.sendRetryCount++;
setTimeout(() => {
this.send(data);
}, this.sendRetryCount * 500);
}
// this.ws.send(JSON.stringify(data));
}
}
新旧setOption是一个相互整合,相互覆盖的过程