如何基于DigitalOcean VPS搭建WebSocket中继服务器实现跨网设备实时通信?
实现跨网络WebSocket视频中继的完整方案
嘿,我来帮你一步步搞定这个跨设备WebSocket视频传输的问题!你的思路完全正确——用DigitalOcean VPS做中继服务器,让两边设备主动连它,完美绕过端口转发的限制。下面是具体的实现步骤,都是实际测试过可行的方案:
一、核心架构确认
我们要实现的是:
Chrome设备A ↔ VPS WebSocket中继服务器 ↔ 树莓派设备B
两边设备都主动连接VPS的公网WebSocket服务,VPS负责把A和B的消息互相转发,这样就能实现实时视频流传输了。
二、VPS端部署WebSocket中继服务器
我推荐用Node.js来写中继,因为它对实时通信支持很好,代码也简单:
先在VPS上安装Node.js和npm
登录你的DigitalOcean droplet,执行命令:sudo apt update && sudo apt install nodejs npm -y编写中继服务器代码
创建一个relay-server.js文件,内容如下:const WebSocket = require('ws'); // 监听8080端口,也可以改成你喜欢的端口 const wss = new WebSocket.Server({ port: 8080 }); // 用来存储两个设备的连接实例 const connections = { deviceA: null, deviceB: null }; wss.on('connection', (ws) => { // 第一步:让设备发送身份标识(deviceA/deviceB)来区分 ws.on('message', (data) => { const msg = data.toString().trim(); if (msg === 'deviceA' || msg === 'deviceB') { connections[msg] = ws; console.log(`✅ 设备${msg}已连接`); return; } // 第二步:转发消息——A发的给B,B发的给A if (connections.deviceA && connections.deviceB) { if (ws === connections.deviceA) { connections.deviceB.send(data); } else if (ws === connections.deviceB) { connections.deviceA.send(data); } } }); // 处理设备断开的情况,清理连接 ws.on('close', () => { if (ws === connections.deviceA) { console.log(`❌ 设备deviceA已断开`); connections.deviceA = null; } else if (ws === connections.deviceB) { console.log(`❌ 设备deviceB已断开`); connections.deviceB = null; } }); }); console.log(`🚀 WebSocket中继服务器已启动,监听端口8080`);安装依赖并启动服务器
执行下面的命令初始化项目、安装WebSocket库:npm init -y npm install ws启动服务器:
node relay-server.js为了让服务器后台运行(避免关闭终端就停止),可以用
pm2:sudo npm install -g pm2 pm2 start relay-server.js pm2 save # 设置开机自启开放VPS防火墙端口
如果你的VPS用的是ufw防火墙,执行命令开放8080端口:sudo ufw allow 8080/tcp sudo ufw reload
三、树莓派(Device B)端代码:发送视频流
树莓派需要主动连接VPS的WebSocket服务,把摄像头采集的视频帧发送给中继服务器。这里用Python实现:
安装依赖库
pip install websockets picamera2 opencv-python-headless编写视频发送代码(比如
video-sender.py)import asyncio import websockets from picamera2 import Picamera2 import cv2 import numpy as np # 替换成你的VPS公网IP VPS_WS_URL = 'ws://你的VPS公网IP:8080' async def send_video_stream(): async with websockets.connect(VPS_WS_URL) as websocket: # 先发送身份标识,告诉中继服务器我是deviceB await websocket.send('deviceB') print("已连接到中继服务器,开始发送视频流...") # 初始化摄像头 picam2 = Picamera2() # 配置分辨率和格式,分辨率越小传输越流畅 picam2.configure(picam2.create_preview_configuration( main={"format": 'RGB888', "size": (640, 480)} )) picam2.start() try: while True: # 采集摄像头帧 frame = picam2.capture_array() # 把帧转成JPEG格式(压缩后体积更小,适合实时传输) _, buffer = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 70]) # 发送二进制数据 await websocket.send(buffer.tobytes()) # 控制帧率,这里设为30帧/秒 await asyncio.sleep(1/30) except Exception as e: print(f"发送出错: {e}") finally: picam2.stop() print("已停止视频发送") if __name__ == '__main__': asyncio.run(send_video_stream())运行代码
python video-sender.py
四、Chrome浏览器(Device A)端:接收并显示视频流
写一个简单的HTML页面,在Chrome里打开就能接收视频流:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>树莓派视频流</title> </head> <body> <h2>来自树莓派的实时视频</h2> <img id="video-img" style="width: 640px; border: 1px solid #ccc;" /> <script> // 替换成你的VPS公网IP const VPS_WS_URL = 'ws://你的VPS公网IP:8080'; const ws = new WebSocket(VPS_WS_URL); const imgElement = document.getElementById('video-img'); ws.onopen = () => { // 发送身份标识,告诉中继服务器我是deviceA ws.send('deviceA'); console.log("已连接到中继服务器"); }; ws.onmessage = (event) => { // 把收到的二进制数据转成Blob,生成URL显示在图片标签里 const blob = new Blob([event.data], { type: 'image/jpeg' }); imgElement.src = URL.createObjectURL(blob); }; ws.onclose = () => { console.log("与中继服务器断开连接"); }; ws.onerror = (error) => { console.error("连接出错:", error); }; </script> </body> </html>
把这个HTML文件保存为video-viewer.html,在Chrome里打开即可看到树莓派传来的实时视频。
五、优化与注意事项
- 视频流畅度优化:如果出现卡顿,可以降低摄像头分辨率(比如改成480x360)、调低JPEG压缩质量(代码里的
70可以改成50-60),或者减少帧率。 - 安全升级:如果要在公网长期使用,建议用WSS(加密WebSocket),给VPS申请Let's Encrypt SSL证书,然后把服务器改成监听443端口,代码里的
ws改成wss。 - 连接稳定性:可以在两边的代码里添加重连逻辑,比如断开后自动尝试重新连接。
- 关于你之前的疑问:你之前想让VPS监听树莓派的本地IP,其实完全不需要——因为树莓派在局域网内,VPS没法主动访问它,正确的做法是让树莓派主动连接VPS的公网服务,这也是上面方案的核心逻辑。
内容的提问来源于stack exchange,提问作者Shariq Azim
相关产品推荐
相关产品推荐

