手机打字,电脑粘贴。通过浏览器在手机上输入文字,一键发送到局域网内的任意电脑。
┌──────────────┐ WebSocket ┌──────────────────────┐
│ 手机浏览器 │ ◄──────────────► │ NSSM 服务 (开机自启) │
│ (Web 前端) │ │ Express + WS │
└──────────────┘ │ UDP 多播发现 │
│ 转发到其他设备 │
└──────┬───────────────┘
│ 内部 WS (/internal)
▼
┌──────────────────────┐
│ paste-helper 进程 │
│ (用户桌面 Session) │
│ 剪贴板 + 模拟按键 │
└──────────────────────┘
服务分为两个进程:
- Server(NSSM 服务,Session 0):网络服务、设备发现、请求转发。不需要桌面权限。
- Helper(用户启动文件夹,Session 1):连接 Server 的内部 WebSocket,收到粘贴指令后在桌面执行剪贴板写入 + Ctrl+V + Enter。有完整的桌面权限。
手机发来的粘贴到本机的请求,Server 通过内部 WS 委托给 Helper 执行,绕过了 Windows 服务的 Session 0 隔离限制。
- 设备发现:UDP 多播自动发现局域网内所有运行本服务的设备
- 文本转发:手机选择目标电脑 → Server 判断是否本机 → Helper 执行粘贴
- 剪贴板保护:粘贴完成后自动恢复原始剪贴板内容
# 安装依赖
npm install
# 编译 TypeScript
npm run build
# 启动服务
npm start
# 手机访问终端输出的地址(例如 http://192.168.1.100:18765)端口默认 18765,可通过环境变量覆盖:
PORT=9999 npm start首次运行时脚本会自动尝试添加 Windows 防火墙入站规则。如果自动添加失败,手动执行:
# PowerShell(管理员)
.\scripts\add-firewall-rule.ps1 -Port 18765或 CMD:
netsh advfirewall firewall add rule name="lan-paste (TCP 18765)" dir=in protocol=tcp localport=18765 action=allow profile=domain,privatePM2 是优秀的 Node.js 进程管理工具,但它的设计目标平台是 Linux / macOS。在 Windows 上,PM2 的开机自启方案经历了多个社区项目,各有各的问题:
| 方案 | 首次发布 | 状态 | 问题 |
|---|---|---|---|
pm2-windows-startup |
2015 | 已停更 | 只写注册表 Run 键,用户登录后才启动、注销就停,不是真正的服务 |
pm2-windows-service |
2016 | 2022 年归档 | 用了 node-windows,重启后 pm2 resurrect 不触发,进程列表为空 |
pm2-installer (jessety) |
2020 | 2022.12 最后更新 | 目前最完整的方案,但依赖老化的 node-windows,在新版 Windows Server 上有兼容问题 |
@nick92/pm2-windows-service |
2023 | 社区 fork | 小范围维护,长期可靠性未知 |
PM2 on Windows 的六个经典踩坑:
PM2_HOME必须设为系统级环境变量(不是用户级),否则 daemon 找不到 dump 文件- npm 全局目录权限:默认在
%APPDATA%\npm,服务账号无权访问 - PowerShell 执行策略:必须设为
RemoteSigned,否则服务脚本被拒绝执行 - 重启后进程列表为空:
pm2 save了但pm2 resurrect没触发(pm2-windows-service已知 bug) EACCES/EPERM权限错误:服务以Local Service运行时无权访问用户家目录- 随机 daemon 崩溃:社区有多例报告,原因未查明
经过实际尝试,PM2 在 Windows 上的整个维护链已半废弃,不适合作为生产级开机自启方案。
NSSM(Non-Sucking Service Manager)是一个纯 Win32 工具,单个 exe 文件(~500KB),从 Windows XP 到 Windows 11 全系兼容。它:
- 不依赖 Node.js 生态,不跟项目绑定
- 直接创建标准 Windows 服务,在用户登录前就启动
- 内置崩溃自动重启、日志轮转
- 可在
services.msc中统一管理 - 安装卸载都只需一条命令
| 对比维度 | PM2 + pm2-installer | NSSM |
|---|---|---|
| 安装步骤 | 5+ 步(PM2_HOME、npm prefix、执行策略、权限……) | 下载一个 exe,执行一条命令 |
| 启动时机 | 用户登录后 | 系统启动时(登录前) |
| 崩溃重启 | 需配置 max_restarts |
原生支持 AppExit Default Restart |
| 日志管理 | pm2 logs + 额外安装 logrotate |
自动重定向到文件,支持轮转 |
| 管理界面 | 命令行 | services.msc GUI + 命令行 |
| 被 Node 版本影响 | 是 | 否 |
| 后续维护成本 | 可能随 Node 升级再次出问题 | 几乎为零 |
从 https://nssm.cc/download 下载 nssm.exe,放到 scripts/nssm/ 目录下:
scripts/nssm/
nssm.exe ← 下载后放这里(已加入 .gitignore)
install.ps1
uninstall.ps1
status.ps1
services.json
launchers/
lan-paste.bat
npm run build确保 dist/server.js 存在。
以管理员身份打开 PowerShell:
.\scripts\nssm\install.ps1脚本会自动:
- 将
nssm.exe复制到C:\Windows\System32\(全局可用) - 读取
services.json,注册所有enabled: true的服务 - 配置开机自启、崩溃自动重启、日志轮转
- 立即启动服务
如果不想用自动化脚本,也可以逐条执行 nssm 命令。以管理员身份打开 CMD 或 PowerShell。
首先确认 nssm 是否已在 PATH 中,以及 node 的位置:
where nssm
where node如果 nssm 不在 PATH 中,安装方式二选一:
REM 方式 A: chocolatey(推荐,自动加入 PATH)
choco install nssm
REM 方式 B: 手动下载并复制到 System32
REM 从 https://nssm.cc/download 下载 nssm.exe
copy scripts\nssm\nssm.exe C:\Windows\System32\确保 dist/server.js 已编译(npm run build),然后执行:
REM 1. 创建服务 — 直接调用 node.exe,不经过 .bat
nssm install lan-paste "C:\Program Files\nodejs\node.exe" dist\server.js
REM 2. 配置服务属性
nssm set lan-paste DisplayName "LAN Paste Service"
nssm set lan-paste Description "局域网快传粘贴服务 -- 手机打字,电脑粘贴 (port 18765)"
nssm set lan-paste AppDirectory G:\GitHub\lan-text-forward
nssm set lan-paste Start SERVICE_AUTO_START
nssm set lan-paste AppExit Default Restart
nssm set lan-paste AppStdout G:\GitHub\lan-text-forward\logs\lan-paste.log
nssm set lan-paste AppStderr G:\GitHub\lan-text-forward\logs\lan-paste-error.log
nssm set lan-paste AppRotateFiles 1
nssm set lan-paste AppRotateSeconds 86400
nssm set lan-paste AppRotateBytes 1048576
nssm set lan-paste AppThrottle 5000
nssm set lan-paste AppEnvironmentExtra "NODE_ENV=production"
REM 3. 启动
nssm start lan-paste注意:
node.exe的路径可能不同。用where node查看实际路径,替换上面的C:\Program Files\nodejs\node.exe。
配置说明:
| 参数 | 作用 |
|---|---|
Start SERVICE_AUTO_START |
开机自启 |
AppExit Default Restart |
进程退出时自动重启 |
AppThrottle 5000 |
两次重启之间至少间隔 5 秒(防止 crash-loop) |
AppRotateFiles 1 |
启用日志轮转 |
AppRotateSeconds 86400 |
每天轮转一次 |
AppRotateBytes 1048576 |
或超过 1 MB 时轮转 |
AppEnvironmentExtra |
注入环境变量到服务进程 |
# 查看状态
.\scripts\nssm\status.ps1
# 或直接
nssm status lan-paste预期输出:
SERVICE STATUS STARTUP
────────────────────────────────────────────────────
lan-paste SERVICE_RUNNING Automatic
# 最近 50 行
Get-Content logs\lan-paste.log -Tail 50
# 实时跟踪
Get-Content logs\lan-paste.log -Wait# 状态总览
.\scripts\nssm\status.ps1
# 停止服务
nssm stop lan-paste
# 启动服务
nssm start lan-paste
# 重启服务(代码更新后)
npm run build # 先编译
nssm restart lan-paste # 再重启
# 图形化管理
services.msc# 临时停止(下次开机仍会自启)
nssm stop lan-paste
# 临时启动
nssm start lan-paste
# 禁止开机自启
nssm set lan-paste Start SERVICE_DEMAND_START
# 恢复开机自启
nssm set lan-paste Start SERVICE_AUTO_STARTnssm stop 只管当前进程,不影响开机自启设置。
NSSM 服务运行在 Session 0,无权操作桌面剪贴板和模拟按键。因此需要一个 Helper 进程在用户桌面 Session 中执行粘贴操作。
Helper 连接 Server 的内部 WebSocket(/internal),等待粘贴指令并执行。
# 注册到启动文件夹(管理员不需要,当前用户即可)
powershell -ExecutionPolicy Bypass -File scripts/register-helper.ps1
# 立即启动(下次开机自动运行)
node dist/helper.js# 查看日志 — 正常应有 "paste helper connected"
Get-Content C:\ProgramData\lan-paste\logs\lan-paste.1.log -Tail 5 | findstr helper如果日志显示 paste helper connected,说明架构正常。手机发送时
日志会显示 paste to self ok (via helper)。
改了 services.json 后,重新运行安装脚本即可:
.\scripts\nssm\install.ps1脚本会检测已存在的服务并更新配置,不会重复注册。
.\scripts\nssm\uninstall.ps1在 services.json 中加一条,编译入口脚本,重跑 install.ps1 即可。
示例:将来要加一个 lan-file 文件传输服务:
{
"name": "lan-file",
"displayName": "LAN File Service",
"description": "局域网文件传输服务 (port 18766)",
"script": "dist/file-server.js",
"env": {
"NODE_ENV": "production",
"PORT": "18766"
},
"enabled": true
}script 是相对于项目根目录的 Node.js 入口文件路径。env 里的键值对会注入为服务进程的环境变量。不需要 .bat 文件。
# 前端测试(React 组件)
pnpm test
# 服务端测试(Helper ↔ Server WS 架构)
pnpm test:server服务端测试覆盖:
- Helper 连接 / 断开跟踪
- 粘贴指令委托 & 成功响应
- 错误响应传递
- 多个 Helper 并发连接
lan-text-forward/
src/ # TypeScript 源码
server.ts # Express + WebSocket 主服务
config.ts # 配置常量(端口、超时、设备 ID 等)
discovery.ts # UDP 多播设备发现
paste.ts # 剪贴板操作 + 模拟按键
client/ # 手机端 Web 前端(React + Vite)
dist/ # tsc 编译产物
scripts/
nssm/ # NSSM 服务管理脚本
add-firewall-rule.* # 防火墙规则脚本
public/ # client 构建后的静态文件(供 Express 托管)
logs/ # 运行日志
这套 NSSM 服务管理模式是零依赖、可移植的。要在另一个 Node.js 项目中使用,复制以下文件并做最小修改:
scripts/nssm/
install.ps1 ← 直接复制,无需修改
uninstall.ps1 ← 直接复制,无需修改
status.ps1 ← 直接复制,无需修改
services.json ← 编辑:改掉 services 列表
外加:从 https://nssm.cc/download 下载 nssm.exe 放到 scripts/nssm/ 下(或 choco install nssm)。
- 复制
scripts/nssm/目录到新仓库 - 编辑
services.json,将services数组替换为你的服务列表(每条声明name、script、env即可,无需.bat文件) - 运行
.\scripts\nssm\install.ps1(管理员 PowerShell)
{
"services": [
{
"name": "my-app",
"displayName": "My App Service",
"description": "My app backend (port 3000)",
"script": "dist/server.js",
"env": {
"NODE_ENV": "production",
"PORT": "3000"
},
"enabled": true
}
]
}script 是相对于项目根目录的 Node.js 入口文件。env 会自动注入为服务进程的环境变量。
- ❌ 不需要
npm install -g pm2 - ❌ 不需要设
PM2_HOME环境变量 - ❌ 不需要改 npm 全局 prefix
- ❌ 不需要改 PowerShell 执行策略
- ✅ 只需要一个 ~500KB 的
nssm.exe
| 层 | 技术 |
|---|---|
| 后端框架 | Express 4 + ws 8 |
| 设备发现 | UDP 多播 (dgram) |
| 剪贴板 | clipboardy |
| 模拟按键 | PowerShell SendKeys (Win) / osascript (Mac) / xdotool (Linux) |
| 前端 | React 19 + TypeScript + Vite + Tailwind CSS |
| 开机自启 | NSSM(Windows 服务) |
MIT