一、效果图:
有两种信息展示,一种是message,一种是心跳机制heart_beat。(创建react项目的过程略)
1、前端:
1.1通过send按钮,前端可以将input输入框的内容信息message传给服务器,并获取返回的内容。
1.2饼图数据是心跳机制heart_beat获取服务器返回的数据5秒更新一次
2、服务器
可以获取前端输入的内容信息
二、实现过程
1、搭建node服务器
1.1首先在新建项目文件夹nodett,然后打开系统命令行cd到nodett文件夹下,执行npm init -y创建项目,文件目录下会出现package.json
npm init -y
1.2 在hbulider编译器(用其他也行)中打开项目文件夹,终端中下载依赖,express、cors 、ws,下载后会出现在node_modules文件夹
npm install express cors ws
1.3在项目中创建app.js,在app.js中输入下面的代码,命令行执行node app.js,出现下图服务器就成功啦
const express = require('express')
const http = require('http');
const app = express()
const cors = require('cors') //跨域设置
// 创建HTTP服务器实例,并将Express应用传递给它
const server = http.createServer(app);
// 正确引入ws模块,并创建WebSocket服务器实例
const WebSocket = require('ws');
const wss = new WebSocket.Server({
server
});
wss.on('connection', (ws) => {
console.log('新WebSocket客户端已连接');
ws.on('message', (message) => messageHandler(wss, ws, message));
ws.on('open', (message) => {
console.log(`open WebSocket消息: ${message}`);
// wss.send(`服务器收到: ${message}`);
});
ws.on('error', (message) => {
console.log(`error WebSocket消息: ${message}`);
// wss.send(`服务器收到: ${message}`);
});
ws.on('close', () => {
console.log('WebSocket客户端已断开连接');
});
});
//
function messageHandler(wss, ws, data) {
console.log('messageHandler===>接收客户端消息', JSON.parse(data))
const {
ModeCode
} = JSON.parse(data)
switch (ModeCode) {
case 'message':
let obj1 = {
type: 'message',
msg: JSON.parse(data).msg
}
console.log('收到消息,当前用户数量:', wss.clients.size)
// 单个用户发送信息
// ws.send(JSON.stringify(obj1))
// 需要发送信息给客户端以此说明连接成功
// 遍历所有连接的客户端并发送消息 实现聊天窗效果
wss.clients.forEach((client) => {
client.send(JSON.stringify(obj1));
});
break;
case 'heart_beat':
console.log('心跳检测')
//返回随机数据,假如有数据库数据就在这块返回
let obj = {
type: 'heart_beat',
data:{
series: [{
name: "Access From",
type: "pie",
radius: "50%",
data: [{
value: Math.random() * 1048,
name: "Search Engine"
},
{
value: Math.random() * 735,
name: "Direct"
},
{
value: Math.random() * 580,
name: "Email"
},
{
value: Math.random() * 484,
name: "Union Ads"
},
{
value: Math.random() * 300,
name: "Video Ads"
},
],
}, ],
}
};
// 需要发送信息给客户端以此说明连接成功
ws.send(JSON.stringify(obj))
break;
}
}
app.use(cors()) //设置允许跨域
//listen启动服务器var server = app
server.listen(8082, function() {
var host = server.address().address
var port = server.address().port
console.log('服务器打开', server.address(), host, port)
})
2、在react中连接服务器并进行信息交互和心跳机制更新,此处创建react项目的过程略。
2.1 react项目中创建api文件夹,创建websocket.js文件,写入下面的代码。将websocket信息封装起来,具体有更新的时间,请求内容的分类,获取服务器数据的处理。
// src/webSocketService.js
class WebSocketService {
constructor(url) {
this.url = url;
this.socket = null;
}
ModeCode = {
MSG: "message", //普通消息
HEART_BEAT: "heart_beat", //心跳检测消息
};
webSocketState = false; //连接状态
heartBeat = {
time: 5000, //心跳检测时间
reconnection: 10000, //断线重连时间
timeout: 3000, //延时等待时间 心跳超时间隔
times: 1, //重连次数,最多5次
};
reconnectionTimer = null; //重连定时器对象
// 连接websocket
connect() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
// 开始心跳检测 不调用这个方法就是不进行心跳检测,发送信息依然可以
this.startHeartBeatFun();
console.log("WebSocket 连接!");
};
// 这里的处理函数在indext页面重写了,为了给饼图赋值,在注释了indext.js的 wsServiceRef.current.socket.onmessage这里才会执行
this.socket.onmessage = (event) => {
// 你可以在这里处理接收到的消息,比如更新组件状态
const data = JSON.parse(event.data);
console.log("WebSocket message received:", data);
switch (data.type) {
case this.ModeCode.MSG: {
console.log("普通消息", data);
break;
}
case this.ModeCode.HEART_BEAT: {
this.webSocketState = true;
console.log("心跳", data);
break;
}
default: {
console.log("其他消息", data);
}
}
};
this.socket.onclose = () => {
this.webSocketState = false;
console.log("WebSocket connection 关闭");
};
this.socket.onerror = (error) => {
console.log("发生错误");
this.webSocketState = false;
// 重连
this.reconnectionWebSocket();
console.error("WebSocket error:", error);
};
}
// 发送信息给服务器
sendMessage(message) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
// this.socket.send(message);
this.socket.send(
JSON.stringify({
ModeCode: this.ModeCode.MSG,
msg: message,
})
);
} else {
console.error(
"WebSocket is not open. Ready state:",
this.socket ? this.socket.readyState : "No socket"
);
}
}
// 开始心跳检测连接
startHeartBeatFun() {
if (this.heartBeat && this.heartBeat.time) {
this.webSocketState = true; //连接
this.startHeartBeat(this.heartBeat.time);
}
}
// 定期心跳检测方法
startHeartBeat(time) {
console.log("startHeartBeat");
setTimeout(() => {
this.socket.send(
JSON.stringify({
ModeCode: this.ModeCode.HEART_BEAT,
msg: new Date(),
})
);
this.waitingServer(); //延时等待服务端响应,判断webSocketState 是否连线成功
}, time);
}
// 延时等待服务端响应,判断webSocketState 是否连线成功
waitingServer() {
// this.webSocketState = false;
console.log("延时等待服务端响应waitingServer");
setTimeout(() => {
console.log(
"延时等待服务端响应waitingServer 的this.webSocketState",
this.webSocketState
);
// 假如是连接状态,持续进行心跳检测
if (this.webSocketState) {
this.startHeartBeat(this.heartBeat.time);
} else {
// 断线
console.log("断线", this.heartBeat.times);
try {
this.close();
} catch (e) {
console.log("连接关闭");
}
console.log("start 重连");
this.reconnectionWebSocket();
console.log("end 重连");
}
}, this.heartBeat.timeout);
}
//断线重连
reconnectionWebSocket() {
console.log("断线重连", this.heartBeat.times);
if (this.heartBeat.times < 5) {
//重连次数,最多5次
this.reconnectionTimer = setTimeout(() => {
this.reConnctWs();
}, this.heartBeat.reconnection);
} else {
console.log("关闭");
this.close();
}
}
reConnctWs() {
if (!this.socket) {
// 第一次执行初始化
this.connect();
}
if (this.socket && this.reconnectionTimer) {
this.heartBeat.times++;
//防止多个计时器同时执行
console.log("防止多个计时器同时执行");
this.reconnectionTimer = null;
this.reconnectionWebSocket();
}
}
close() {
if (this.socket) {
this.socket.close();
}
}
}
export default WebSocketService;
2.2 下载echarts依赖,封装echarts
项目命令行执行
npm install echarts
项目文件夹中新建components文件夹,创建echarts文件夹,创建index.js,写入以下代码封装echarts组件。
import * as echarts from "echarts";
import Reactk, { useRef, useEffect } from "react";
const MyEcharts = ({ style, chartData }) => {
const echartsRef = useRef(); //获取dom实例
let echartsObj = useRef(null); //用于响应式变量,但是不触发页面刷新,普通变量无法响应式
useEffect(() => {
// 基于准备好的dom,初始化echarts实例
echartsObj.current = echarts.init(echartsRef.current);
// 绘制图表
echartsObj.current.setOption(chartData);
// 调整图表大小
const resizeChart = () => {
if (echartsObj.current) {
echartsObj.current.resize();
}
};
// 监听窗口大小变化
window.addEventListener("resize", resizeChart);
});
return <div className="echartsRefaa" style={style} ref={echartsRef}></div>;
};
export default MyEcharts;
2.3在需要进行websocket连接的页面写入以下代码,本例为home/indext.js,调用Home
//indext.js
import React, { useEffect, useState, useRef } from "react";
import WebSocketService from "../../api/websocket";
import MyEcharts from "../../components/echarts";
//这里本机服务器地址是127.0.0.1,8082端口在node里面有规定,node中的cors依赖可以实现跨域
const webSocketUrl = "ws://127.0.0.1:8082"; // 替换为你的WebSocket服务器URL,ws:不能省略
const Home = () => {
// echarts饼图数据
let chartData = {
series: [
{
name: "Access From",
type: "pie",
radius: "50%",
data: [
{ value: 1048, name: "Search Engine" },
{ value: 735, name: "Direct" },
{ value: 580, name: "Email" },
{ value: 484, name: "Union Ads" },
{ value: 300, name: "Video Ads" },
],
},
],
};
const [chartDataInfo, setChartDataInfo] = useState(chartData); //chartData3
const [messages, setMessages] = useState([]);
const wsServiceRef = useRef(null); // 使用 useRef 来持有 WebSocketService 实例
useEffect(() => {
wsServiceRef.current = new WebSocketService(webSocketUrl);
wsServiceRef.current.connect();
// 处理接收到的消息
const handleMessage = (message) => {
console.log(message, JSON.parse(message));
if (JSON.parse(message).type === "message") {
setMessages((prevMessages) => [
...prevMessages,
JSON.parse(message).msg,
]);
} else {
setChartDataInfo(JSON.parse(message).data);
}
// 图3chartData2数据
};
wsServiceRef.current.socket.onmessage = (event) =>
handleMessage(event.data);
// 清理函数,组件卸载时关闭WebSocket连接
return () => {
if (wsServiceRef.current) {
wsServiceRef.current.close();
}
};
}, [webSocketUrl]); // 依赖项数组只包含 webSocketUrl
// 输入内容传回服务器
const sendMessage = (event) => {
event.preventDefault(); //阻止表单的默认提交行为
const input = event.target.elements.messageInput; //从触发事件的目标元素(event.target)中获取名为 messageInput 的表单元素
const wsService = wsServiceRef.current;
// 检查 WebSocket 实例是否存在且连接已打开
if (
wsService &&
wsService.socket &&
wsService.socket.readyState === WebSocket.OPEN
) {
wsService.sendMessage(input.value);
} else {
console.error("WebSocket is not ready to send messages.");
}
input.value = ""; // 清空输入框
};
return (
<div>
<h1>WebSocket Demo</h1>
<ul>
{messages.map((message, index) => (
<li key={index}>{message}</li>
))}
</ul>
<form onSubmit={sendMessage}>
<input type="text" name="messageInput" placeholder="Send a message" />
<button type="submit">Send</button>
</form>
<div className="graph-two flex ai-c jc-sb">
{chartDataInfo && (
<MyEcharts
style={{ width: "50%", height: "260px" }}
chartData={chartDataInfo}
></MyEcharts>
)}
</div>
</div>
);
};
export default Home;