websocket展示输入数据,心跳机制(本地node.js+react)

一、效果图:

有两种信息展示,一种是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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值