使用 WebSocket 实现手机控制端和电脑展示端的实时通信,包含断线重连功能。

主要分为三个部分:WebSocket 服务器、电脑展示端页面和手机控制端页面。

1. WebSocket 服务器(Node.js)

首先需要搭建一个 WebSocket 服务器作为中间层,转发控制指令:

javascript 复制代码
// server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

// 存储连接的客户端,区分控制端和展示端
let controller = null;  // 手机控制端
let display = null;    // 电脑展示端

console.log('WebSocket服务器启动,端口: 8080');

wss.on('connection', (ws) => {
  console.log('新客户端连接');

  // 接收客户端消息
  ws.on('message', (message) => {
    try {
      const data = JSON.parse(message.toString());
      
      // 客户端类型注册
      if (data.type === 'register') {
        if (data.role === 'controller') {
          controller = ws;
          console.log('手机控制端已连接');
          // 通知控制端是否已有展示端连接
          ws.send(JSON.stringify({
            type: 'status',
            hasDisplay: !!display
          }));
        } else if (data.role === 'display') {
          display = ws;
          console.log('电脑展示端已连接');
          // 通知展示端是否已有控制端连接
          ws.send(JSON.stringify({
            type: 'status',
            hasController: !!controller
          }));
        }
      } 
      // 转发控制指令
      else if (data.type === 'control' && controller === ws && display) {
        console.log('转发控制指令:', data);
        display.send(JSON.stringify(data));
      }
    } catch (e) {
      console.error('消息处理错误:', e);
    }
  });

  // 客户端断开连接
  ws.on('close', () => {
    if (ws === controller) {
      controller = null;
      console.log('手机控制端已断开');
      // 通知展示端控制端已断开
      if (display) {
        display.send(JSON.stringify({
          type: 'status',
          hasController: false
        }));
      }
    } else if (ws === display) {
      display = null;
      console.log('电脑展示端已断开');
      // 通知控制端展示端已断开
      if (controller) {
        controller.send(JSON.stringify({
          type: 'status',
          hasDisplay: false
        }));
      }
    }
  });

  // 错误处理
  ws.on('error', (error) => {
    console.error('WebSocket错误:', error);
  });
});
2. 电脑展示端页面(display.html)
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;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 100vh;
      margin: 0;
      transition: background-color 0.5s;
    }
    #status {
      position: fixed;
      top: 20px;
      padding: 10px 20px;
      border-radius: 5px;
      background-color: #f0f0f0;
    }
    .connected {
      background-color: #4CAF50;
      color: white;
    }
    .disconnected {
      background-color: #f44336;
      color: white;
    }
    #displayArea {
      width: 80%;
      height: 60%;
      border: 2px solid #333;
      border-radius: 10px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 24px;
      transition: all 0.3s;
    }
  </style>
</head>
<body>
  <div id="status" class="disconnected">未连接到控制端</div>
  <div id="displayArea">等待控制指令...</div>

  <script>
    let ws;
    let reconnectInterval;
    const reconnectDelay = 3000; // 重连间隔3秒

    // 连接WebSocket服务器
    function connect() {
      // 关闭可能存在的旧连接
      if (ws) {
        ws.close();
      }

      // 获取当前页面的主机地址,确保使用相同的IP
      const host = window.location.hostname;
      ws = new WebSocket(`ws://${host}:8080`);

      // 连接成功
      ws.onopen = () => {
        console.log('WebSocket连接成功');
        // 注册为展示端
        ws.send(JSON.stringify({
          type: 'register',
          role: 'display'
        }));
        // 清除重连计时器
        clearInterval(reconnectInterval);
        updateStatus(true);
      };

      // 接收消息
      ws.onmessage = (event) => {
        const data = JSON.parse(event.data);
        console.log('收到消息:', data);
        
        // 处理状态更新
        if (data.type === 'status') {
          updateStatus(data.hasController);
        }
        // 处理控制指令
        else if (data.type === 'control') {
          handleControl(data.command, data.value);
        }
      };

      // 连接关闭
      ws.onclose = () => {
        console.log('WebSocket连接关闭');
        updateStatus(false);
        // 启动重连机制
        startReconnect();
      };

      // 连接错误
      ws.onerror = (error) => {
        console.error('WebSocket错误:', error);
        updateStatus(false);
      };
    }

    // 启动重连机制
    function startReconnect() {
      console.log(`将在${reconnectDelay/1000}秒后尝试重连...`);
      reconnectInterval = setInterval(connect, reconnectDelay);
    }

    // 更新连接状态显示
    function updateStatus(connected) {
      const statusEl = document.getElementById('status');
      if (connected) {
        statusEl.textContent = '已连接到控制端';
        statusEl.className = 'connected';
      } else {
        statusEl.textContent = '未连接到控制端,正在尝试重连...';
        statusEl.className = 'disconnected';
      }
    }

    // 处理控制指令
    function handleControl(command, value) {
      const displayArea = document.getElementById('displayArea');
      
      switch(command) {
        case 'changeColor':
          document.body.style.backgroundColor = value;
          displayArea.textContent = `背景色已更改为: ${value}`;
          break;
        case 'changeText':
          displayArea.textContent = value;
          break;
        case 'scale':
          displayArea.style.transform = `scale(${value})`;
          displayArea.textContent = `缩放比例: ${value}`;
          break;
        case 'rotate':
          displayArea.style.transform = `rotate(${value}deg)`;
          displayArea.textContent = `旋转角度: ${value}°`;
          break;
        default:
          console.log('未知指令:', command);
      }
    }

    // 初始化连接
    connect();
  </script>
</body>
</html>
3. 手机控制端页面(controller.html)
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  <title>手机控制端</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      padding: 20px;
      margin: 0;
      background-color: #f5f5f5;
    }
    #status {
      padding: 10px;
      border-radius: 5px;
      text-align: center;
      margin-bottom: 20px;
    }
    .connected {
      background-color: #4CAF50;
      color: white;
    }
    .disconnected {
      background-color: #f44336;
      color: white;
    }
    .control-group {
      background-color: white;
      padding: 15px;
      border-radius: 10px;
      margin-bottom: 15px;
      box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    }
    h3 {
      margin-top: 0;
      color: #333;
    }
    button {
      width: 100%;
      padding: 12px;
      margin-bottom: 10px;
      border: none;
      border-radius: 5px;
      background-color: #2196F3;
      color: white;
      font-size: 16px;
      cursor: pointer;
    }
    button:hover {
      background-color: #0b7dda;
    }
    input[type="color"], input[type="text"] {
      width: 100%;
      padding: 10px;
      margin-bottom: 10px;
      border: 1px solid #ddd;
      border-radius: 5px;
      box-sizing: border-box;
    }
    input[type="range"] {
      width: 100%;
      margin: 15px 0;
    }
    .range-value {
      text-align: center;
      font-weight: bold;
    }
  </style>
</head>
<body>
  <div id="status" class="disconnected">未连接到展示端</div>

  <div class="control-group">
    <h3>背景颜色控制</h3>
    <input type="color" id="bgColor" value="#ffffff">
    <button onclick="sendCommand('changeColor', document.getElementById('bgColor').value)">
      更改背景色
    </button>
  </div>

  <div class="control-group">
    <h3>文本控制</h3>
    <input type="text" id="displayText" placeholder="输入要显示的文本">
    <button onclick="sendCommand('changeText', document.getElementById('displayText').value)">
      更新显示文本
    </button>
  </div>

  <div class="control-group">
    <h3>缩放控制</h3>
    <input type="range" id="scale" min="0.5" max="2" step="0.1" value="1">
    <div class="range-value" id="scaleValue">1.0</div>
    <button onclick="sendCommand('scale', document.getElementById('scale').value)">
      应用缩放
    </button>
  </div>

  <div class="control-group">
    <h3>旋转控制</h3>
    <input type="range" id="rotate" min="0" max="360" step="15" value="0">
    <div class="range-value" id="rotateValue">0°</div>
    <button onclick="sendCommand('rotate', document.getElementById('rotate').value)">
      应用旋转
    </button>
  </div>

  <script>
    let ws;
    let reconnectInterval;
    const reconnectDelay = 3000; // 重连间隔3秒
    let isConnected = false;

    // 更新滑块显示值
    document.getElementById('scale').addEventListener('input', function() {
      document.getElementById('scaleValue').textContent = this.value;
    });
    document.getElementById('rotate').addEventListener('input', function() {
      document.getElementById('rotateValue').textContent = this.value + '°';
    });

    // 连接WebSocket服务器
    function connect() {
      // 关闭可能存在的旧连接
      if (ws) {
        ws.close();
      }

      // 获取当前页面的主机地址,确保使用相同的IP
      const host = window.location.hostname;
      ws = new WebSocket(`ws://${host}:8080`);

      // 连接成功
      ws.onopen = () => {
        console.log('WebSocket连接成功');
        // 注册为控制端
        ws.send(JSON.stringify({
          type: 'register',
          role: 'controller'
        }));
        // 清除重连计时器
        clearInterval(reconnectInterval);
      };

      // 接收消息
      ws.onmessage = (event) => {
        const data = JSON.parse(event.data);
        console.log('收到消息:', data);
        
        // 处理状态更新
        if (data.type === 'status') {
          isConnected = data.hasDisplay;
          updateStatus(isConnected);
        }
      };

      // 连接关闭
      ws.onclose = () => {
        console.log('WebSocket连接关闭');
        isConnected = false;
        updateStatus(false);
        // 启动重连机制
        startReconnect();
      };

      // 连接错误
      ws.onerror = (error) => {
        console.error('WebSocket错误:', error);
        isConnected = false;
        updateStatus(false);
      };
    }

    // 启动重连机制
    function startReconnect() {
      console.log(`将在${reconnectDelay/1000}秒后尝试重连...`);
      reconnectInterval = setInterval(connect, reconnectDelay);
    }

    // 更新连接状态显示
    function updateStatus(connected) {
      const statusEl = document.getElementById('status');
      if (connected) {
        statusEl.textContent = '已连接到展示端';
        statusEl.className = 'connected';
      } else {
        statusEl.textContent = '未连接到展示端,正在尝试重连...';
        statusEl.className = 'disconnected';
      }
    }

    // 发送控制指令
    function sendCommand(command, value) {
      if (!isConnected || !ws) {
        alert('未连接到展示端,请稍后重试');
        return;
      }

      try {
        ws.send(JSON.stringify({
          type: 'control',
          command: command,
          value: value
        }));
        console.log(`发送指令: ${command} = ${value}`);
      } catch (e) {
        console.error('发送指令失败:', e);
        alert('发送指令失败,请重试');
      }
    }

    // 初始化连接
    connect();
  </script>
</body>
</html>

使用说明

  1. 环境准备

    • 安装 Node.js
    • 安装 WebSocket 模块:npm install ws
  2. 启动服务器

    • 运行命令:node server.js
    • 服务器将在 8080 端口启动
  3. 部署页面

    • 将 display.html 和 controller.html 放在 Web 服务器(如 Nginx、Apache)或使用简单的 HTTP 服务器
    • 例如:使用 Node.js 的 http-server:npx http-server
  4. 使用方法

    • 确保手机和电脑连接到同一网络
    • 在电脑上打开展示端页面:http://[服务器IP]:8080/display.html
    • 在手机上打开控制端页面:http://[服务器IP]:8080/controller.html
    • 现在可以通过手机控制电脑页面的各种属性了

断线重连机制说明

  • 当连接断开时,客户端会自动尝试重连
  • 重连间隔为 3 秒,可通过修改reconnectDelay变量调整
  • 重连过程中会显示状态提示
  • 重连成功后会自动恢复通信,无需手动操作

这个实现提供了基本的控制功能(颜色、文本、缩放、旋转),你可以根据需要扩展更多控制指令和 UI 元素。

相关推荐
学传打活1 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续3)
微信公众平台·1024程序员节·汉字·昆仑正义文化
学传打活6 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续1)
微信公众平台·1024程序员节·汉字·昆仑正义文化
fangzhanpeng16814 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心16 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u01030552722 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u01030552723 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝24 天前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u01030552724 天前
Java图像处理实战指南
人工智能·1024程序员节
u01030552725 天前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u0103055271 个月前
Java实用类应用精讲
人工智能·1024程序员节