字节笔记本
2026年8月29日
没有公网 IP:用 MQTT-Web-Terminal 远程进树莓派
家用树莓派、公司里的边缘盒子,往往都没有公网 IP。内网穿透能解决,但要额外服务、证书和端口映射。MQTT-Web-Terminal 换了条路:设备端和浏览器都连同一台 MQTT Broker,终端输入输出走主题转发,设备本身不用暴露任何端口。
仓库在 lewangdev/MQTT-Web-Terminal,设备侧是 Python + PTY,浏览器侧是 xterm.js + MQTT over WebSocket。
它怎么通的
整体就三块:
- 设备端(树莓派):
main.py用pty.fork()拉起一个 shell,再用paho-mqtt连 Broker。收到/device/<id>/terminal/input就往 PTY 写键入,读到 shell 输出就发到/device/<id>/terminal/output。窗口大小变化走/device/<id>/terminal/resize。 - 浏览器端:
app.js用 xterm.js 画终端,通过 WebSocket(常见是wss://...:8084/mqtt)连同一个 Broker,订阅 output、发布 input/resize。 - MQTT Broker:两边都主动连出去。设备在 NAT 后面也没关系,只要能访问 Broker。
默认示例用的是带 TLS 的 EMQX 类云 Broker(仓库里带了 emqxsl-ca.crt,端口 8883),你也可以换成自建 Mosquitto / EMQX,只要同时支持 TCP MQTT(设备)和 WebSocket MQTT(浏览器)。
先准备 Broker
选一个你能拿到账号密码的 MQTT 服务,例如 EMQX Cloud 免费实例,或自己的 EMQX/Mosquitto。记下:
- 主机名(设备用,例如
xxx.emqxsl.com) - MQTT TLS 端口(常见
8883) - WebSocket TLS 地址(常见
wss://xxx.emqxsl.com:8084/mqtt) - 用户名 / 密码
- CA 证书(若用云厂商 TLS,把仓库里的证书换成你自己的,或关掉 TLS 仅在内网测)
主题按设备 ID 隔离。仓库默认 DEVICE_ID = "raspberrypi",多台机器就改成不同 ID。
设备端:树莓派上跑终端代理
克隆仓库并装依赖:
git clone https://github.com/lewangdev/MQTT-Web-Terminal.git
cd MQTT-Web-Terminal
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtrequirements.txt 目前只有 paho-mqtt==1.6.1。
改 main.py 顶部配置:
DEVICE_ID = "raspberrypi" # 多机时改成唯一名
DEVICE_SHELL = "bash" # 或 "sh"
MQTT_HOST = "你的-broker-主机"
MQTT_PORT = 8883
MQTT_USER = "mqtt"
MQTT_PASSWD = "你的密码"
MQTT_USE_TLS = True用 TLS 时,把 client.tls_set("emqxsl-ca.crt") 指到你的 CA;自签或测试环境可按需调整 tls_insecure_set。确认后启动:
python3 main.py进程会一直挂着:连上 Broker 后订阅 input/resize,并开线程把 PTY 输出推到 output 主题。建议用 systemd 或 tmux 保活,开机自启更省心。
浏览器端
在同一仓库里(有 Node 的电脑上)按 README 装依赖并启动静态页,浏览器打开本机 8000 端口。
改 app.js:用户名、口令、deviceId、mqttUrl(WebSocket 地址)要和设备端一致。 deviceId 必须和 DEVICE_ID 相同。连上后状态显示 connected,按 Enter 激活终端。复制粘贴用 Ctrl+Shift+X / Ctrl+Shift+V。
安全上别踩坑
这条链路等于把 shell 挂到公网 Broker 上,务必:
- 强口令 + ACL:Broker 上限制该用户只能读写对应 device 主题,别开匿名。
- TLS:生产环境别用明文 MQTT;浏览器侧优先 wss。
- 别用 root 跑设备端:用普通用户,需要时再 sudo。
- 仓库示例里的空口令是占位符,部署前换成自己的,也别把改过的配置提交回去。
需要多人访问时,前面再加一层登录(反向代理 Basic Auth / SSO)更稳妥。
和内网穿透差在哪
| MQTT-Web-Terminal | frp / ngrok 一类 | |
|---|---|---|
| 设备是否开端口 | 否,只出站连 Broker | 通常要开客户端连中继 |
| 终端 UI | 自带 xterm 网页 | 仍要自己 SSH / WebTTY |
| 依赖 | 一个 MQTT 服务 | 中继服务 + 隧道配置 |
| 多设备 | 靠不同 DEVICE_ID 主题 | 多条隧道或不同端口 |
如果你已经有 MQTT(家居、IoT 网关),这套几乎零额外基础设施;如果只是临时 SSH,传统隧道可能更轻。两者不互斥,可以按场景选。
小结
没有公网 IP 时,不必硬上端口映射。让树莓派和浏览器都连 MQTT,用主题把 PTY 的输入输出桥过去,网页里就能进 shell。上手步骤:配好 Broker,改 main.py 在设备上跑,改 app.js 在浏览器连同一套凭据和设备 ID。源码见 https://github.com/lewangdev/MQTT-Web-Terminal ,终端前端思路可参考 https://github.com/cs01/pyxtermjs 。