当前位置: 首页 > news >正文

使用 WebSocket 实现手机控制端与电脑展示端的实时通信,支持断线重连、状态同步和双向数据交互。(最优方案)

系统采用前端技术栈,通过 Node.js 搭建 WebSocket 服务端,控制端 (手机) 和展示端 (电脑) 均为网页应用。

1. 服务端实现 (WebSocket 服务器)

首先创建 WebSocket 服务,用于转发控制端和展示端之间的消息:

// server.js
const WebSocket = require('ws');
const http = require('http');
const express = require('express');const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });// 存储连接的客户端:控制端和展示端
let controller = null;
let display = null;// 静态文件服务
app.use(express.static('public'));// WebSocket连接处理
wss.on('connection', (ws) => {console.log('新客户端连接');// 客户端身份验证ws.on('message', (data) => {try {const message = JSON.parse(data);// 身份注册if (message.type === 'register') {if (message.role === 'controller') {controller = ws;console.log('控制端已连接');sendStatusUpdate(); // 更新连接状态} else if (message.role === 'display') {display = ws;console.log('展示端已连接');sendStatusUpdate(); // 更新连接状态}} // 转发控制命令else if (message.type === 'control' && ws === controller && display) {console.log('转发控制命令:', message);display.send(JSON.stringify({type: 'control',action: message.action,data: message.data,timestamp: new Date().getTime()}));} // 转发状态更新else if (message.type === 'status' && ws === display && controller) {console.log('转发状态更新:', message);controller.send(JSON.stringify({type: 'status',status: message.status,data: message.data,timestamp: new Date().getTime()}));}} catch (error) {console.error('消息处理错误:', error);}});// 客户端断开连接处理ws.on('close', () => {if (ws === controller) {console.log('控制端已断开');controller = null;} else if (ws === display) {console.log('展示端已断开');display = null;}sendStatusUpdate(); // 更新连接状态});// 错误处理ws.on('error', (error) => {console.error('WebSocket错误:', error);});
});// 发送连接状态更新给所有客户端
function sendStatusUpdate() {const status = {controllerConnected: !!controller,displayConnected: !!display,bothConnected: !!controller && !!display,timestamp: new Date().getTime()};if (controller) {controller.send(JSON.stringify({ type: 'connectionStatus', ...status }));}if (display) {display.send(JSON.stringify({ type: 'connectionStatus', ...status }));}
}// 启动服务器
const PORT = process.env.PORT || 8080;
server.listen(PORT, () => {console.log(`服务器运行在 http://localhost:${PORT}`);console.log(`控制端: http://localhost:${PORT}/controller.html`);console.log(`展示端: http://localhost:${PORT}/display.html`);
});
2. 公共工具类 (WebSocket 连接管理)

创建一个公共的 WebSocket 管理工具,处理连接、重连和消息发送:

// public/js/websocket-manager.js
class WebSocketManager {constructor(role, reconnectInterval = 3000) {this.role = role; // 'controller' 或 'display'this.reconnectInterval = reconnectInterval;this.ws = null;this.connected = false;this.callbacks = {connectionStatus: [],control: [],status: []};this.connect();}// 建立连接connect() {// 关闭现有连接if (this.ws) {this.ws.close();}// 连接WebSocket服务器const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';const url = `${protocol}//${window.location.host}`;this.ws = new WebSocket(url);// 连接成功this.ws.onopen = () => {console.log('WebSocket连接成功');this.connected = true;// 注册客户端角色this.send({ type: 'register', role: this.role });};// 接收消息this.ws.onmessage = (event) => {try {const message = JSON.parse(event.data);this.handleMessage(message);} catch (error) {console.error('解析消息错误:', error);}};// 连接关闭this.ws.onclose = () => {console.log('WebSocket连接关闭,尝试重连...');this.connected = false;// 自动重连setTimeout(() => this.connect(), this.reconnectInterval);};// 连接错误this.ws.onerror = (error) => {console.error('WebSocket错误:', error);};}// 处理接收到的消息handleMessage(message) {switch (message.type) {case 'connectionStatus':this.callbacks.connectionStatus.forEach(callback => callback(message));break;case 'control':this.callbacks.control.forEach(callback => callback(message));break;case 'status':this.callbacks.status.forEach(callback => callback(message));break;}}// 发送消息send(message) {if (this.connected && this.ws) {try {this.ws.send(JSON.stringify(message));return true;} catch (error) {console.error('发送消息失败:', error);return false;}}console.warn('无法发送消息,连接未建立');return false;}// 注册事件回调on(eventType, callback) {if (this.callbacks[eventType]) {this.callbacks[eventType].push(callback);} else {console.warn(`未知的事件类型: ${eventType}`);}}// 关闭连接close() {if (this.ws) {this.ws.close();this.ws = null;this.connected = false;}}
}
3. 控制端实现 (手机端)

创建控制端界面,用于发送控制命令到展示端:

<!-- public/controller.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>控制端</title><style>body {font-family: Arial, sans-serif;margin: 20px;max-width: 500px;margin: 0 auto;padding: 20px;}.status {padding: 10px;margin-bottom: 20px;border-radius: 5px;text-align: center;}.disconnected {background-color: #ffcccc;color: #cc0000;}.connected {background-color: #ccffcc;color: #006600;}.controls {display: grid;grid-template-columns: 1fr 1fr;gap: 10px;margin-bottom: 20px;}button {padding: 15px;font-size: 16px;cursor: pointer;border: none;border-radius: 5px;background-color: #4CAF50;color: white;}button:hover {background-color: #45a049;}.data-section {margin-top: 20px;}.item {padding: 10px;border: 1px solid #ddd;border-radius: 5px;margin-bottom: 10px;display: flex;justify-content: space-between;align-items: center;}input, textarea {width: 100%;padding: 10px;margin-bottom: 10px;border: 1px solid #ddd;border-radius: 5px;}.response {margin-top: 10px;padding: 10px;background-color: #f0f0f0;border-radius: 5px;}</style>
</head>
<body><h1>设备控制端</h1><!-- 连接状态 --><div class="status disconnected" id="connectionStatus">未连接</div><!-- 控制按钮 --><div class="controls"><button id="btnStart">启动</button><button id="btnStop">停止</button><button id="btnReset">重置</button><button id="btnUpdate">更新配置</button></div><!-- 数据管理 --><div class="data-section"><h3>数据管理</h3><input type="text" id="dataInput" placeholder="输入数据..."><button id="btnAdd">添加数据</button><button id="btnQuery">查询数据</button><div id="dataList"><!-- 数据项将在这里动态生成 --></div></div><!-- 响应区域 --><div class="response-section"><h3>展示端响应</h3><div id="response" class="response">等待响应...</div></div><script src="js/websocket-manager.js"></script><script>// 初始化WebSocket管理器,角色为控制端const wsManager = new WebSocketManager('controller');// DOM元素const connectionStatusEl = document.getElementById('connectionStatus');const responseEl = document.getElementById('response');const dataListEl = document.getElementById('dataList');const dataInputEl = document.getElementById('dataInput');// 监听连接状态更新wsManager.on('connectionStatus', (status) => {console.log('连接状态更新:', status);if (status.bothConnected) {connectionStatusEl.textContent = '已连接到展示端';connectionStatusEl.className = 'status connected';} else if (status.controllerConnected) {connectionStatusEl.textContent = '等待展示端连接...';connectionStatusEl.className = 'status disconnected';} else {connectionStatusEl.textContent = '未连接';connectionStatusEl.className = 'status disconnected';}});// 监听展示端返回的状态wsManager.on('status', (message) => {console.log('收到展示端状态:', message);responseEl.textContent = `[${new Date(message.timestamp).toLocaleTimeString()}] ${JSON.stringify(message.data)}`;// 如果是数据列表更新,刷新本地列表if (message.action === 'query' || message.action === 'add' || message.action === 'delete') {updateDataList(message.data);}});// 更新数据列表function updateDataList(items) {dataListEl.innerHTML = '';if (!items || items.length === 0) {dataListEl.innerHTML = '<div class="item">无数据</div>';return;}items.forEach((item, index) => {const itemEl = document.createElement('div');itemEl.className = 'item';itemEl.innerHTML = `<span>${item}</span><button class="delete-btn" data-index="${index}">删除</button>`;dataListEl.appendChild(itemEl);});// 绑定删除按钮事件document.querySelectorAll('.delete-btn').forEach(btn => {btn.addEventListener('click', (e) => {const index = parseInt(e.target.dataset.index);sendControlCommand('delete', { index });});});}// 发送控制命令function sendControlCommand(action, data = {}) {if (wsManager.connected) {wsManager.send({type: 'control',action,data});responseEl.textContent = `已发送 ${action} 命令,等待响应...`;} else {responseEl.textContent = '未连接到服务器,无法发送命令';}}// 绑定按钮事件document.getElementById('btnStart').addEventListener('click', () => {sendControlCommand('start');});document.getElementById('btnStop').addEventListener('click', () => {sendControlCommand('stop');});document.getElementById('btnReset').addEventListener('click', () => {sendControlCommand('reset');});document.getElementById('btnUpdate').addEventListener('click', () => {sendControlCommand('update', { config: '新配置参数' });});document.getElementById('btnAdd').addEventListener('click', () => {const value = dataInputEl.value.trim();if (value) {sendControlCommand('add', { value });dataInputEl.value = '';}});document.getElementById('btnQuery').addEventListener('click', () => {sendControlCommand('query');});</script>
</body>
</html>
4. 展示端实现 (电脑端)

创建展示端界面,接收控制命令并返回处理结果:

<!-- public/display.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>展示端</title><style>body {font-family: Arial, sans-serif;margin: 20px;}.status {padding: 15px;margin-bottom: 20px;border-radius: 5px;font-size: 18px;}.disconnected {background-color: #ffcccc;color: #cc0000;}.connected {background-color: #ccffcc;color: #006600;}.control-panel {border: 1px solid #ddd;padding: 20px;border-radius: 5px;margin-bottom: 20px;}.data-display {border: 1px solid #ddd;padding: 20px;border-radius: 5px;}.event-log {margin-top: 20px;padding: 10px;background-color: #f9f9f9;border-radius: 5px;height: 200px;overflow-y: auto;}.log-entry {margin-bottom: 5px;padding: 5px;border-bottom: 1px solid #eee;}.item {padding: 10px;border: 1px solid #eee;border-radius: 5px;margin-bottom: 10px;}</style>
</head>
<body><h1>展示端</h1><!-- 连接状态 --><div class="status disconnected" id="connectionStatus">未连接到控制端</div><!-- 当前状态 --><div class="control-panel"><h2>当前状态: <span id="currentState">未启动</span></h2><div id="actionResult">等待控制命令...</div></div><!-- 数据展示 --><div class="data-display"><h3>数据列表</h3><div id="dataList"><!-- 数据项将在这里动态生成 --></div></div><!-- 事件日志 --><div class="event-log"><h3>事件日志</h3><div id="eventLog"></div></div><script src="js/websocket-manager.js"></script><script>// 初始化WebSocket管理器,角色为展示端const wsManager = new WebSocketManager('display');// 模拟数据存储let dataStore = [];let currentState = '未启动';// DOM元素const connectionStatusEl = document.getElementById('connectionStatus');const currentStateEl = document.getElementById('currentState');const actionResultEl = document.getElementById('actionResult');const dataListEl = document.getElementById('dataList');const eventLogEl = document.getElementById('eventLog');// 监听连接状态更新wsManager.on('connectionStatus', (status) => {console.log('连接状态更新:', status);if (status.bothConnected) {connectionStatusEl.textContent = '已连接到控制端';connectionStatusEl.className = 'status connected';logEvent('已连接到控制端');} else if (status.displayConnected) {connectionStatusEl.textContent = '等待控制端连接...';connectionStatusEl.className = 'status disconnected';logEvent('等待控制端连接...');} else {connectionStatusEl.textContent = '未连接';connectionStatusEl.className = 'status disconnected';logEvent('连接已断开');}});// 监听控制命令wsManager.on('control', (message) => {console.log('收到控制命令:', message);logEvent(`收到命令: ${message.action}`);handleControlCommand(message.action, message.data);});// 处理控制命令function handleControlCommand(action, data) {let result = {};switch (action) {case 'start':currentState = '运行中';result = { status: 'success', message: '系统已启动' };break;case 'stop':currentState = '已停止';result = { status: 'success', message: '系统已停止' };break;case 'reset':currentState = '未启动';dataStore = [];updateDataList();result = { status: 'success', message: '系统已重置', data: dataStore };break;case 'update':result = { status: 'success', message: '配置已更新', data: data };break;case 'add':if (data && data.value) {dataStore.push(data.value);updateDataList();result = { status: 'success', message: '数据已添加', data: dataStore };} else {result = { status: 'error', message: '添加失败,数据为空' };}break;case 'delete':if (data && typeof data.index === 'number' && data.index >= 0 && data.index < dataStore.length) {const deleted = dataStore.splice(data.index, 1);updateDataList();result = { status: 'success', message: `已删除: ${deleted[0]}`, data: dataStore };} else {result = { status: 'error', message: '删除失败,索引无效' };}break;case 'query':result = { status: 'success', message: '数据查询成功', data: dataStore };break;default:result = { status: 'error', message: `未知命令: ${action}` };}// 更新UIcurrentStateEl.textContent = currentState;actionResultEl.textContent = `命令 ${action} 已处理: ${result.message}`;// 向控制端返回处理结果wsManager.send({type: 'status',action,status: result.status,data: result});}// 更新数据列表显示function updateDataList() {dataListEl.innerHTML = '';if (dataStore.length === 0) {dataListEl.innerHTML = '<div class="item">无数据</div>';return;}dataStore.forEach((item, index) => {const itemEl = document.createElement('div');itemEl.className = 'item';itemEl.textContent = `${index + 1}. ${item}`;dataListEl.appendChild(itemEl);});}// 添加日志条目function logEvent(message) {const logEl = document.createElement('div');logEl.className = 'log-entry';logEl.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;eventLogEl.prepend(logEl); // 添加到日志顶部// 限制日志数量if (eventLogEl.children.length > 50) {eventLogEl.removeChild(eventLogEl.lastChild);}}// 初始化数据列表updateDataList();</script>
</body>
</html>

实现说明

这个系统通过 WebSocket 实现了手机控制端和电脑展示端的实时通信,主要特点包括:

  1. 实时通信:使用 WebSocket 实现全双工通信,支持即时命令发送和状态反馈
  2. 断线重连:WebSocket 连接断开后会自动尝试重新连接
  3. 状态同步:两端连接状态实时更新,用户可以直观了解当前连接情况
  4. 完整的增删改查:支持数据的添加、删除、查询等操作
  5. 双向数据交互:控制端发送命令,展示端处理后返回结果
  6. 事件日志:展示端记录所有接收的命令和系统事件

使用方法

  1. 安装依赖:npm install ws express
  2. 启动服务器:node server.js
  3. 在手机浏览器中访问:http://服务器IP:8080/controller.html
  4. 在电脑浏览器中访问:http://服务器IP:8080/display.html

系统启动后,两端会自动尝试连接,连接成功后即可通过手机端控制电脑端的展示内容。

http://www.dtcms.com/a/524786.html

相关文章:

  • 快递鸟 MCP Server:AI 工具解锁 物流 API 能力,开启智能物流新生态
  • UV Python 包和项目管理工具
  • 使用 Quill 实现编辑器功能
  • 企业网站建设的可行性图片编辑软件加文字
  • 零基础网站建设视频教程做淘宝美工的网站
  • 微米级光斑分析仪市场报告:政策、趋势与前景深度解析
  • 达梦 DM Database 集群:从概念到开发场景
  • 面向社科研究者:用深度学习做因果推断(一)
  • 站长seo计费系统比较好的网页模板网站
  • 【学习笔记】大模型
  • ES7243E 模拟音频转I2S输入给BES I2S_Master数据运行流程分析
  • 虚拟内存与RAM
  • 广州花都区网站建设长沙seo优化排名推广
  • 广告公司网站模版做一家网站要多少钱
  • 【Linux知识】Linux文本操作相关命令行
  • Port设置功能开发实践: Pyside6 MVC架构与Model/View/Delegate模式的应用
  • 白之家低成本做网站深圳比较好的建网站公司
  • 深度学习一些知识点(指标+正则化)
  • 企业官方网站建设的作用仿牌 镜像网站
  • java实现多线程分片下载超大文件,支持HTTPS。
  • 数据结构和算法(十)--B树
  • 从零起步学习MySQL || 第九章:从数据页的角度看B+树及MySQL中数据的底层存储原理(结合常见面试题深度解析)
  • HTTP 与 SOCKS5 代理协议:企业级选型指南与工程化实践
  • 新华三H3CNE网络工程师认证—STP状态机与收敛过程
  • 从零起步学习MySQL || 第十章:深入了解B+树及B+树的性能优势(结合底层数据结构与数据库设计深度解析)
  • 阿里云服务器网站备案台州北京网站建设
  • 眼镜网站建设深圳网站设计精选刻
  • CF1060 CD
  • 莱西做网站公司繁体网站模板
  • 学校网站建设培训心得如何登陆建设银行信用卡网站