客户管理系统是springboot+vue,包含了http和websocket. springboot 端口80,vue前端端口8001,前端通过代理统一到了localhost:8001端口。
因为需要在前端播放RTSP流,采用了nodejs express+websocket+ffmpeg做后端,把RTSP流转换成flv格式传递到前端。前后端调试,可能是ws前后端跨域可能没处理好,代码如下。
以下是nodejs代码:
"use strict";
const express = require('express');
const http = require('http');
const bodyParser = require('body-parser');
const logger = require('morgan');
const path = require('path');
const colors = require('colors');
const crypto = require('crypto');
const md5 = crypto.createHash('md5');
const dbconfig = require('../dbconfig');
const mysql = require('mysql');
const RtspStream = require('rtsp-stream');
const { spawn } = require('child_process');
const ffmpeg = require('fluent-ffmpeg');
const WebSocket = require('ws');
const cors = require('cors');
const corsOptions = {origin: 'ws://localhost:8888/wim'};
const app = express();
const server = http.createServer(app);
//const wss = new WebSocket.Server({ server });
const wss = new WebSocket.Server({ server, path: '/wim', perMessageDeflate: false, handleProtocols: (protocols) => ['wim'] });
const rtspStreamUrl = 'rtsp://192.168.0.101:8554/test'; // 替换为实际的 RTSP 流地址
//const rtspStreamUrl = 'http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8';
let ffmpegCommand; // 存储 FFmpeg 进程的变量
// 处理客户端的 WebSocket 连接
wss.on('connection', (ws) => {
console.log('wss.on+27');
ws.on('message', (message) => {
console.log('29');
console.log(typeof message, message);
//message转换成String
if (Buffer.isBuffer(message)) {
message = message.toString();
};
console.log(typeof message, message);
if (message === 'startProxy') {
console.log('31')
if (ffmpegCommand) {
// 如果已经有正在运行的 FFmpeg 进程,则结束它
ffmpegCommand.kill();
}
// 创建 FFmpeg 进程,并将 RTSP 转码为 FLV
ffmpegCommand = spawn('ffmpeg', [
'-i', rtspStreamUrl,
'-c:v', 'copy',
'-c:a', 'aac',
'-f', 'flv',
//'-movflags', 'frag_keyframe+empty_moov',
//'pipe:1'
]);
ffmpegCommand.on('start', () => {
console.log('FFmpeg process has started---55');
});
ffmpegCommand.on('error', (err) => {
console.error('FFmpeg error:', err);
ws.send('proxyError'); // Notify the frontend of proxy error
});
ffmpegCommand.on('exit', (code, signal) => {
if (code === 0) {
console.log('FFmpeg process has exited successfully');
} else {
console.error('FFmpeg process has exited with code:', code);
ws.send('proxyError'); // Notify the frontend of proxy error
}
});
ffmpegCommand.stdout.on('data', (data) => {
// You can optionally log the FFmpeg output here
console.log('FFmpeg output:', data.toString());
});
ffmpegCommand.stderr.on('data', (data) => {
// You can optionally log the FFmpeg error output here
console.error('FFmpeg error output:', data.toString());
});
ffmpegCommand.on('close', (code, signal) => {
console.log('FFmpeg process has been closed');
ws.send('proxyEnd'); // Notify the frontend that proxy has ended
});
ffmpegCommand.stdout.pipe(ws); // Pipe FFmpeg stdout to the WebSocket
console.log('64');
ws.send('proxyStarted'); // 通知前端代理已开始
}
});
});
// 静态文件目录
app.use(express.static(path.join(__dirname, 'public')));
// 处理 POST 请求中的 JSON 数据
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// 日志输出
app.use(logger('dev'));
//处理资源跨域,只允许特定域名的请求
app.use(cors(corsOptions));
//n处理客户端的 HTTP 请求
app.get('/stream', (req, res) => {
res.setHeader('Content-Type', 'video/x-flv');
res.send('Please use WebSocket to connect for video streaming.'); // 请使用 WebSocket 连接进行视频流传输
});
const port = 8888; // 你可以修改端口号
server.listen(port, () => {
console.log(`服务器已启动,监听端口 ${port}`);
});
以下是前端VUE页面
<template>
<div>
<video ref="videoElement" controls></video>
</div>
</template>
<script>
import flvjs from 'flv.js';
export default {
data() {
return {
socket: null,
videoElement: null,
flvPlayer: null,
};
},
mounted() {
this.videoElement = this.$refs.videoElement;
// 在视频播放器元素上添加点击事件
// WebSocket代理服务器地址
//const proxyUrl = 'ws://localhost:8001/wim';
const proxyUrl = 'ws://' + window.location.hostname + ':8001/wim'
// 创建WebSocket连接
//this.socket = new WebSocket(proxyUrl);
this.socket = new WebSocket(proxyUrl, 'wim');
//只是为了增加一步,方便看程序
console.log('front-end 32');
if (this.socket.readyState === WebSocket.OPEN) {
console.log('WebSocket 连接已经打开');
} else {
console.log('WebSocket 连接未打开');
};
this.socket.onopen = () => {
// 在连接打开时执行的代码
console.log('WebSocket connection opened');
// 发送请求告诉服务器开始代理RTSP流
this.socket.send('startProxy');
//this.socket.send(JSON.stringify('startProxy'));
};
if (this.socket.readyState === WebSocket.OPEN) {
console.log('WebSocket 连接已经打开');
} else {
console.log('WebSocket 连接未打开');
};
// 当WebSocket连接打开时
this.socket.addEventListener('open', () => {
console.log('WebSocket connection opened');
// 发送请求告诉服务器开始代理RTSP流
this.socket.send('startProxy');
//this.socket.send(JSON.stringify('startProxy'));
});
if (this.socket.readyState === WebSocket.OPEN) {
console.log('WebSocket 连接已经打开');
} else {
console.log('WebSocket 连接未打开');
};
// 当接收到消息时
this.socket.addEventListener('message', (event) => {
console.log('message39');
const message = event.data;
if (message === 'proxyStarted') {
// 代理已开始,使用flv.js播放器播放视频流
if (this.videoElement && flvjs.isSupported()) {
this.flvPlayer = flvjs.createPlayer({
type: 'flv',
//url: 'http://localhost:8001/stream'
//url: 'rtsp://192.168.0.103:8554/test'
url: 'ws://localhost:8001/wim'
});
this.flvPlayer.attachMediaElement(this.videoElement);
this.flvPlayer.load();
this.flvPlayer.play();
this.flvPlayer.on(flvjs.Events.ERROR, (event, data) => {
console.error('FLV.js error:', data);
});
this.flvPlayer.on(flvjs.Events.LOADING_COMPLETE, () => {
console.log('Video loading complete');
});
}
} else if (message === 'proxyError') {
// 代理发生错误
console.error('Proxy error');
} else if (message === 'proxyEnd') {
// 代理已结束
console.log('Proxy ended');
}
});
// 当WebSocket连接错误时
this.socket.addEventListener('error', () => {
console.log('WebSocket connection Error');
});
},
beforeDestroy() {
if (this.socket) {
// 关闭WebSocket连接
this.socket.close();
}
if (this.flvPlayer) {
// 销毁flv.js播放器
this.flvPlayer.unload();
this.flvPlayer.detachMediaElement();
this.flvPlayer.destroy();
}
}
};
</script>
端口代理:
module.exports = {
// 开发环境代理服务器
devProxy: {
host: 'localhost',//'0.0.0.0', // ip/localhost都可以访问
port: 8001
},
// 后端服务器地址
servers: {
proxyApi: 'http://localhost:80/',
//proxyApi: 'http://localhost:8000/',
//ais: 'http://192.168.55.100:8888/ais',
//ais: 'http://192.168.2.100:8000/ais',
ais: 'http://localhost:8888/ais',
//ais: 'http://192.168.177.181:8888/ais',
mysqlRouter:'http://localhost:8888/mysqlRouter',
stream: 'http://localhost:8888/stream',
wim: 'http://localhost:8888/wim
}
}
问题:前端页面调试,前端页面websocket看起来跑了两轮,第一轮并没有正常打开,但是代码过了一遍。第二遍nodejs后端停止运行了,前端websocket是打开的。
最后能帮向日葵远程调试
基于new bing部分指引作答:
根据您提供的代码和描述,我注意到可能存在以下问题:
跨域配置错误:在您的Node.js后端代码中,使用了cors模块来处理跨域请求,但是您定义的corsOptions中的origin值是ws://localhost:8888/wim。然而,WebSocket的跨域配置应该使用Access-Control-Allow-Origin标头来处理,而不是origin字段。您可以尝试修改corsOptions为以下内容:
const corsOptions = {
origin: 'http://localhost:8001', // 更新为正确的Vue前端地址
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type'],
};
然后,在app.use(cors(corsOptions))之前添加以下代码:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', corsOptions.origin);
res.header('Access-Control-Allow-Headers', corsOptions.allowedHeaders.join(', '));
next();
});
这样设置可以确保WebSocket连接的跨域配置正确。
调用ws.send()的时机不正确:在您的前端Vue代码中,您在WebSocket的open事件中发送了startProxy消息。但是,此时WebSocket连接可能还没有完全建立。正确的做法是在open事件之后的onopen回调中发送消息。您可以尝试修改如下:
this.socket.onopen = () => {
// 在连接打开时执行的代码
console.log('WebSocket connection opened');
// 发送请求告诉服务器开始代理RTSP流
this.socket.send('startProxy');
//this.socket.send(JSON.stringify('startProxy'));
};
通过这种方式,您可以确保在WebSocket连接完全建立后再发送消息。
端口代理配置错误:在您提供的端口代理配置文件中,缺少了一个引号,导致wim的配置不完整。请将以下行:
wim: 'http://localhost:8888/wim
改为:
wim: 'http://localhost:8888/wim',
这样可以修正配置错误。
请尝试按照上述修改建议进行调整,看看问题是否得到解决。
WebSocket跨域和路径匹配是最关键的两点。确认前后端连接参数一致,并处理好CORS
配置express的CORS中间件:
在Node.js后端的代码中,你已经引入了cors模块并使用了corsOptions对象作为cors中间件的配置。但是,在代码中似乎没有实际应用该中间件。你可以将app.use(cors(corsOptions));
添加到express应用程序的配置部分,确保CORS中间件生效。
检查Vue前端的WebSocket地址:
请确保前端Vue代码中使用的WebSocket地址(proxyUrl)是正确的。你已经设置为'ws://' + window.location.hostname + ':8001/wim'
,这将根据前端所在的域名动态生成WebSocket地址。如果你的Vue前端运行在不同的域名上,请确保你使用的URL与实际的Node.js WebSocket服务器地址匹配。
确定WebSocket连接是否成功打开:
在Vue前端的代码中,可以通过监听WebSocket的open
事件来确定WebSocket连接是否成功打开。当WebSocket连接打开时,会执行相应的回调函数,并在其中发送请求告知服务器开始代理RTSP流。你可以在websocket.onopen
或this.socket.addEventListener('open', () => {})
中添加日志输出,以确保WebSocket连接已成功建立。
检查网络连接和防火墙设置:
确保Node.js后端和Vue前端之间的网络连接是可用的,并且没有防火墙或网络代理阻止WebSocket通信。你可以尝试使用其他工具或方法测试网络连接,确保能够在相同的网络环境中进行WebSocket通信。
如果上述方法不起作用,还有一些额外的调试选项和注意事项:
error
)和关闭(close
)事件中添加相应的错误处理程序,以便捕获并打印出任何与WebSocket连接相关的错误。在使用WebSocket进行跨域对接时,您需要在后端和前端进行相应的配置来允许跨域通信。下面是一种可能的解决方案:
后端(Spring Boot)配置:
在Spring Boot应用程序中,确保WebSocket配置允许跨域访问。您可以使用Spring Security或自定义配置来实现跨域请求的授权。
在Spring Boot的WebSocket配置类中,将setAllowedOrigins("*")设置为允许来自任何域的跨域请求。
确保后端服务器在响应握手请求时包含Access-Control-Allow-Origin标头,并设置为允许的前端域。
前端(Vue.js)配置:
在Vue.js应用程序中,使用WebSocket连接时,确保指定正确的后端WebSocket服务器地址。
在Vue.js的网络请求中,确保请求标头包含Origin标头,以便服务器知道请求的来源。
在Vue.js的网络请求中,使用withCredentials: true设置来发送凭据信息(如Cookie)。
确保前端服务器在响应握手请求时返回正确的CORS标头,允许WebSocket连接。
Node.js后端配置:
在Node.js后端应用程序中,使用WebSocket模块(如ws)进行配置和处理WebSocket连接。
在WebSocket服务器设置中,将origin选项设置为允许的前端域。
在WebSocket握手时,确保服务器响应包含Access-Control-Allow-Origin标头,并设置为允许的前端域。
请注意,具体的配置可能因您的应用程序和网络环境而有所不同。以上仅提供了一般性的指导方针。根据您的具体情况和需求,您可能需要根据实际情况进行调整和进一步的配置。
另外,跨域通信可能涉及到安全性和授权问题,请确保在实现跨域通信时采取适当的安全措施,以防止恶意攻击和数据泄漏。
源于chatGPT仅供参考
根据你提供的代码,有几个问题可能导致第一轮前端页面无法正常打开WebSocket连接并且后端停止运行:
1. **跨域设置**:在你的代码中,在Node.js后端的WebSocket配置中,你指定了`path`参数为`/wim`来匹配Vue前端的WebSocket连接请求。但是在前端的Vue页面中,你创建WebSocket连接时,并没有在URL中指定路径,只是指定了`ws://localhost:8001/wim`作为WebSocket的URL。请确保两端的路径匹配,可以在前端的WebSocket URL中明确指定路径。
2. **端口代理**:根据你提供的配置文件,你将前端的Vue页面代理到了`localhost:8001`端口,但是在前端的Vue页面代码中,你使用了`window.location.hostname + ':8001'`来拼接WebSocket的URL,这可能会导致WebSocket的URL不正确。建议直接使用`localhost:8001`或者将前端代理到与后端WebSocket端口号相同的端口(即8888)。
3. **WebSocket连接状态判断**:在前端的Vue页面中,你多次使用了`if (this.socket.readyState === WebSocket.OPEN)`来判断WebSocket连接是否已经打开。然而,这些判断语句都是在Vue组件的`mounted()`钩子函数中执行的,此时WebSocket的连接可能还没有完成。你应该在WebSocket的`onopen`回调函数中执行相关的操作,以确保WebSocket连接已经打开后再进行后续的操作。
尝试解决这些问题,然后重新运行代码,查看是否能够正常建立WebSocket连接并且后端不会停止运行。如果问题仍然存在,请提供错误信息或详细的日志内容,以便更好地帮助你解决问题。
不知道你这个问题是否已经解决, 如果还没有解决的话:问题: 请问你在前端调试过程中遇到了什么具体的问题?你期望的优化是什么?
在前端调试过程中,我遇到的具体问题有以下几个方面:
页面加载速度过慢:当页面中的资源较多或者文件较大时,页面的加载速度会变得很慢,导致用户体验较差。 优化方案:通过压缩CSS和JavaScript文件,合并文件请求,使用CDN等方式来提升页面加载速度;
页面不兼容不同浏览器:不同浏览器对HTML、CSS、JavaScript的解析和渲染存在差异,导致页面在不同浏览器上展示效果不同。 优化方案:进行浏览器兼容性测试,使用 CSS hack 或者使用 CSS 预处理器来解决不同浏览器兼容性问题;
JavaScript代码错误:由于复杂的业务逻辑或者不规范的编码导致JavaScript代码出现错误,导致页面功能无法正常运行。 优化方案:使用调试工具(如Chrome开发者工具)来定位代码错误,进行合理的异常处理和错误处理;
性能优化问题:页面的性能优化是提升用户体验的关键,包括DOM操作、网络请求、JavaScript运行等。 优化方案:减少DOM操作的次数,使用事件委托来提高性能,优化网络请求,减少不必要的请求,合理使用缓存等。
以上问题的解决方案需要根据具体的情况进行调试和优化,通过不断地排查和测试,找到问题的根源,并进行相应的优化措施。在调试过程中,可以使用浏览器的开发者工具、调试器、日志记录工具等来辅助定位问题并加快调试的效率。