dtns-rpc-fast
v1.3.0
Published
Upload-as-reverse-download WebRTC RPC framework with dynamic chunking, server-driven retransmission and resumable transfer.
Maintainers
Readme
dtns-rpc-fast
上传当反向下载的 WebRTC 高速 RPC 框架 —— 动态切片 + 服务端驱动重发 + 断点续传,让浏览器、Node、Android 三端用同一套协议跑 GB 级文件与实时命令。
中文
🎯 这是什么
dtns-rpc-fast 是一套基于 WebRTC DataChannel 的高速 RPC 框架。核心创新:
上传 = 反向下载。 同一套协议、同一份代码,同时支持两个方向的流式传输。
传统方案里,上传和下载是两套逻辑:下载用分片 + 滑动窗口,上传又得另写一套。dtns-rpc-fast 抽象出 StreamPusher(推流方) 和 StreamReceiver(接收方) 两个类,让它们角色互换:
| 方向 | 推流方 | 接收方 | |---|---|---| | 下载(Server → Client) | 服务端 | 客户端 | | 上传(Client → Server) | 客户端 | 服务端 |
同一份代码,两个方向,完全对称。
✨ 核心特性
- 🔁 上传当反向下载:对称协议,一套代码解决双向流式传输
- 📊 动态切片:32KB → 512KB 自适应网络质量,无重传时增大、重传多时减小
- 📡 服务端驱动重发:接收方检测到坏片/丢片,主动发
STREAM_RETRY,精确重传 - 🔄 断点续传:
streamId持久化,网络抖动/进程重启自动恢复 - 🌐 NAT 穿透:基于 WebRTC,无需公网 IP、无需端口映射
- ⚡ 二进制分片:无 Base64 膨胀,接近原生吞吐
- 🗜️ 大 JSON 自动降级 + gzip 压缩:小结果直接返回,大结果走流 + 压缩,带宽节省最高 90%
- 🔐 DTLS-SRTP 强制加密:传输层默认加密
- 🧩 跨平台统一:浏览器 / Node / Android 共用同一份
rpc-protocol+rpc-core - 🎛️ 存储可插拔:
sink/source接口,OPFS / fs / Android 桥接随意切换
📦 安装
npm i dtns-rpc-fast socket.io-client simple-signal-client jszipNode.js 端额外需要原生 WebRTC:
npm i wrtc浏览器端从 CDN 拿:
<script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/simple-signal-client@3/dist/simple-signal-client.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/browser/dtns-rpc-fast.min.js"></script>🚀 快速开始
Node.js 客户端
import { createNodeClient, FsDownloadSink, FsUploadSource } from 'dtns-rpc-fast';
const client = createNodeClient('room-demo', 'http://your-signal-server');
await client.connect();
// 1. 普通 RPC
const r = await client.call('/hello', { name: 'Alice' });
console.log(r); // { msg: 'hello Alice' }
// 2. 下载文件
await client.download(
'/file/get',
{ name: 'big.zip' },
new FsDownloadSink({ savePath: './big.zip' }),
(got, total) => console.log(`${got}/${total}`)
);
// 3. 上传文件
await client.upload(
new FsUploadSource('./big.zip'),
'/file/put',
{ userId: 1 }
);
client.close();Node.js 服务端
import { createNodeServer } from 'dtns-rpc-fast';
import fs from 'node:fs';
import path from 'node:path';
const server = createNodeServer('room-demo', 'http://your-signal-server', {
uploadDir: './uploads',
});
// 注册 RPC handler
server.on('/hello', ({ params }) => ({ msg: 'hello ' + params.name }));
server.on('/file/get', async ({ params }) => {
const fp = path.resolve('./shared', params.name);
const stat = fs.statSync(fp);
return {
name: params.name,
size: stat.size,
async open() { return { name: params.name, size: stat.size }; },
async read(offset, length) {
const fd = fs.openSync(fp, 'r');
const buf = Buffer.alloc(Math.min(length, stat.size - offset));
const n = fs.readSync(fd, buf, 0, buf.length, offset);
fs.closeSync(fd);
return new Uint8Array(buf.buffer, buf.byteOffset, n);
},
async close() {},
};
});
await server.start();浏览器客户端
<script>
const client = new DTNS.RPCClient('room-demo', 'http://your-signal-server', {}, {
socketIo: io,
SimpleSignalClient: window.SimpleSignalClient,
createDownloadSink: () => new DTNS.BrowserStorage.OpfsDownloadSink({ dir: 'downloads' }),
wrapUploadSource: (src) =>
src instanceof Blob ? new DTNS.BrowserStorage.FileUploadSource(src) : src,
});
await client.connect();
// 调用 RPC
const r = await client.call('/hello', { name: 'Bob' });
// 下载
await client.download('/file/get', { name: 'big.zip' });
// 上传
const file = document.querySelector('input[type=file]').files[0];
await client.upload(file, '/file/put', {});
</script>底层 API(完全控制)
如果你想自己管理连接生命周期:
import { RPCClient, FsDownloadSink, FileUploadSource } from 'dtns-rpc-fast';
import { io } from 'socket.io-client';
import SimpleSignalClient from 'simple-signal-client';
const client = new RPCClient('room-demo', 'http://your-signal-server', {}, {
socketIo: io,
SimpleSignalClient,
rtcOptions: {
iceServers: [{ urls: 'turn:your-turn-server:3478' }],
},
createDownloadSink: () => new FsDownloadSink({ dir: './downloads' }),
wrapUploadSource: (src) =>
src instanceof Blob ? new FileUploadSource(src) : src,
});📡 API 参考
RPCClient
| 方法 | 说明 |
|---|---|
| connect(timeoutMs?) | 连接信令服务器并建立 WebRTC 通道 |
| close() | 关闭连接 |
| call<T>(url, params?, opts?) | 发起 RPC 调用,返回 Promise |
| download(url, params?, sink?, onProgress?) | 下载文件/流 |
| upload(source, url, extraParams?, onProgress?) | 上传文件/流 |
事件(通过 addEventListener):
ready:WebRTC 通道就绪close:连接关闭error:出错
RPCServer
| 方法 | 说明 |
|---|---|
| start() | 启动服务端 |
| stop() | 停止 |
| on(url, handler) | 注册 RPC handler |
事件:ready、peer、peer-close
FsDownloadSink / FsUploadSource
Node 端文件读写。FsDownloadSink 构造参数:
savePath:完整保存路径dir:目标目录(自动用原文件名)
BrowserStorage
浏览器端存储。包含:
OpfsDownloadSink:写 OPFS(不支持时回退内存)FileUploadSource:从File/Blob读
AndroidDownloadSink / AndroidUploadSource
Android WebView 端通过 AndroidBridge 读写本地文件。详见 rpc-bridge.html。
📁 子路径导出
// 主入口(默认走 Node 版)
import { RPCClient, RPCServer } from 'dtns-rpc-fast';
// 明确 Node 版
import { FsDownloadSink, FsUploadSource } from 'dtns-rpc-fast/node';
// 浏览器版(打包器环境)
import { RPCClient, BrowserStorage } from 'dtns-rpc-fast/browser';| 子路径 | 用途 |
|---|---|
| dtns-rpc-fast | 默认入口,Node 走 index.js,浏览器走 UMD |
| dtns-rpc-fast/node | Node 专用(含 FsDownloadSink 等) |
| dtns-rpc-fast/browser | 浏览器 UMD |
| dtns-rpc-fast/android | Android WebView 桥接页 |
⚙️ 配置项
const DEFAULTS = {
// 切片控制
initChunk: 32 * 1024, // 初始切片 32KB
minChunk: 16 * 1024, // 下限 16KB
maxChunk: 512 * 1024, // 上限 512KB
growFactor: 1.5, // 顺利时增长倍率
shrinkFactor: 0.5, // 重传时减小倍率
chunkAlign: 4096, // 对齐到 4KB
// 网络
highWater: 10 * 1024 * 1024, // bufferedAmount 背压阈值
hardMaxFrame: 96 * 1024, // 单帧硬上限
// 大 JSON 分片阈值
autoStreamThreshold: 16 * 1024, // 超过走 stream
gzipThreshold: 4 * 1024, // 超过 gzip
enableGzip: true,
maxJsonSize: 256 * 1024 * 1024, // 硬上限
// 超时
signalTimeout: 20000,
rpcTimeout: 30000,
transferTimeout: 3600000,
ackStallTimeout: 5000,
pusherStallTimeout: 10000,
maxRetries: 20,
};🏗️ 架构
┌─────────────────────────────────────────────────────────┐
│ 应用层:RPC handler(自定义业务逻辑) │
├─────────────────────────────────────────────────────────┤
│ 协议层:dtns-rpc-fast │
│ - RPC 帧:REQ / RES / ERR / STREAM / ACK / RETRY │
│ - 分片:动态切片 + 滑动窗口 + 停滞检测 │
│ - 存储抽象:sink / source 接口 │
├─────────────────────────────────────────────────────────┤
│ 传输层:WebRTC DataChannel (SCTP over DTLS) │
│ - 强制加密 (DTLS-SRTP) │
│ - 可靠传输 (SCTP ordered mode) │
│ - NAT 穿透 (STUN/TURN/ICE) │
├─────────────────────────────────────────────────────────┤
│ 信令层:Socket.IO + simple-signal │
└─────────────────────────────────────────────────────────┘🧪 测试
# 启动信令服务器(自行部署)
# 例如基于 simple-signal-server
# 启动 Server
ROOM=room-demo node node-demo/server-demo.js
# 启动 Client
ROOM=room-demo node node-demo/client-demo.js🔒 安全
- 传输加密:DTLS-SRTP,强制,无需配置
- E2EE:P2P 直连和 TURN 中继场景下都是 E2EE(TURN 只转发密文)
- 信任锚:信令服务器。信令走 HTTPS/WSS + 你控制的服务器 → 达到 E2EE 级别
- 增强(可选):fingerprint 外带校验,防信令 MITM
🤝 贡献
欢迎提交 Issue 和 PR。
- Gitee:https://gitee.com/dtnsman/dtns.rpc-fast
- Issues:https://gitee.com/dtnsman/dtns.rpc-fast/issues
📄 License
GPL-3.0-or-later
English
🎯 What Is It
dtns-rpc-fast is a high-speed RPC framework built on WebRTC DataChannel. Core innovation:
Upload = Reverse Download. One protocol, one codebase, two directions.
📦 Install
npm i dtns-rpc-fast socket.io-client simple-signal-client jszip
npm i wrtc # Node.js only🚀 Quick Start
import { createNodeClient, FsDownloadSink, FsUploadSource } from 'dtns-rpc-fast';
const client = createNodeClient('room-demo', 'http://your-signal-server');
await client.connect();
const r = await client.call('/hello', { name: 'Alice' });
await client.download('/file/get', { name: 'big.zip' }, new FsDownloadSink({ savePath: './big.zip' }));
await client.upload(new FsUploadSource('./big.zip'), '/file/put', {});📄 License
GPL-3.0-or-later
