js-rpc2 2.4.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/doc/http.md ADDED
@@ -0,0 +1,202 @@
1
+ # HTTP 使用文档
2
+
3
+ ## 概述
4
+
5
+ js-rpc 支持通过 HTTP 协议进行 RPC 调用,使用 Koa 框架作为服务器端实现。HTTP 传输适用于 Web 应用、微服务通信等场景。
6
+
7
+ ## 核心组件
8
+
9
+ ### 服务器端
10
+
11
+ 1. [createRpcServerKoaRouter](../src/server.js#L73) - 在 Koa 路由中创建 RPC 服务器
12
+ 2. [createRpcServerHelper](../src/lib.js#L467) - 创建 RPC 服务器助手(内部使用)
13
+
14
+ ### 客户端
15
+
16
+ 1. [createRpcClientHttp](../src/lib.js#L726) - 创建 HTTP RPC 客户端
17
+
18
+ ## 完整示例
19
+
20
+ ### 1. 服务器端代码 (server.js)
21
+
22
+ ```js
23
+ import { createServer } from 'http'
24
+ import Koa from 'koa'
25
+ import Router from '@koa/router'
26
+ import { createRpcServerKoaRouter } from 'js-rpc2/src/server.js'
27
+
28
+ // 创建 Koa 应用和路由
29
+ const app = new Koa()
30
+ const router = new Router()
31
+
32
+ // 应用中间件
33
+ app.use(router.routes())
34
+ app.use(router.allowedMethods())
35
+
36
+ // 启动 HTTP 服务器
37
+ const server = createServer(app.callback()).listen(9000)
38
+ console.log('Server listening on port 9000')
39
+
40
+ // 定义可远程调用的函数
41
+ class RpcApi {
42
+ /**
43
+ * 示例方法:带回调的异步函数
44
+ * @param {string} name - 名称参数
45
+ * @param {(data: string) => void} update - 回调函数,用于报告进度
46
+ */
47
+ async hello(name, update) {
48
+ for (let i = 0; i < 3; i++) {
49
+ // 调用回调函数报告进度
50
+ update(`progress ${i}`)
51
+ await new Promise((resolve) => setTimeout(resolve, 1000))
52
+ }
53
+ return `hello ${name}`
54
+ }
55
+
56
+ /**
57
+ * 示例方法:处理二进制数据
58
+ * @param {Uint8Array} buffer - 二进制数据
59
+ */
60
+ async processBuffer(buffer) {
61
+ // 对数据进行处理,例如截取部分数据
62
+ return buffer.slice(0, Math.min(100, buffer.length))
63
+ }
64
+
65
+ /**
66
+ * 示例方法:处理复杂对象
67
+ * @param {Object} data - 复杂对象
68
+ */
69
+ async processData(data) {
70
+ return {
71
+ received: data,
72
+ timestamp: new Date().toISOString(),
73
+ processed: true
74
+ }
75
+ }
76
+ }
77
+
78
+ // 创建 API 实例
79
+ const extension = new RpcApi()
80
+
81
+ // 创建 RPC 服务器
82
+ createRpcServerKoaRouter({
83
+ path: '/rpc', // RPC 服务路径
84
+ router: router, // Koa 路由实例
85
+ extension: extension, // 可调用的函数集合
86
+ rpcKey: 'optional-secret-key' // 可选的安全密钥
87
+ })
88
+ ```
89
+
90
+ ### 2. 客户端代码 (client.js)
91
+
92
+ ```js
93
+ import { createRpcClientHttp } from 'js-rpc2/src/client.js'
94
+
95
+ // 创建 RPC 客户端
96
+ /** @type{RpcApi} */
97
+ const rpc = createRpcClientHttp({
98
+ url: 'http://127.0.0.1:9000/rpc', // RPC 服务地址
99
+ rpcKey: 'optional-secret-key' // 可选的安全密钥(需与服务端一致)
100
+ })
101
+
102
+ // 使用示例
103
+ async function example() {
104
+ try {
105
+ // 调用带回调的异步函数
106
+ const result = await rpc.hello('World', (progress) => {
107
+ console.log('Progress:', progress)
108
+ })
109
+ console.log('Result:', result)
110
+
111
+ // 调用处理二进制数据的函数
112
+ const buffer = new Uint8Array([1, 2, 3, 4, 5])
113
+ const processedBuffer = await rpc.processBuffer(buffer)
114
+ console.log('Processed buffer:', processedBuffer)
115
+
116
+ // 调用处理复杂对象的函数
117
+ const data = { name: 'test', value: 42 }
118
+ const processedData = await rpc.processData(data)
119
+ console.log('Processed data:', processedData)
120
+
121
+ } catch (error) {
122
+ console.error('RPC call failed:', error)
123
+ }
124
+ }
125
+
126
+ // 执行示例
127
+ example()
128
+ ```
129
+
130
+ ## API 说明
131
+
132
+ ### 服务器端 API
133
+
134
+ #### createRpcServerKoaRouter
135
+
136
+ 在 Koa 路由中创建 RPC 服务器。
137
+
138
+ ```js
139
+ /**
140
+ * @param {{
141
+ * path: string;
142
+ * router: Router<any, {}>;
143
+ * rpcKey?:string;
144
+ * logger?:(msg:string)=>void;
145
+ * extension: {asyncLocalStorage:AsyncLocalStorage;};
146
+ * }} param
147
+ */
148
+ export function createRpcServerKoaRouter(param)
149
+ ```
150
+
151
+ 参数说明:
152
+ - `path`: RPC 服务的路由路径
153
+ - `router`: Koa 路由实例
154
+ - `rpcKey`: 可选的安全密钥,用于验证客户端请求
155
+ - `logger`: 可选的日志函数
156
+ - `extension`: 包含可远程调用函数的对象
157
+
158
+ ### 客户端 API
159
+
160
+ #### createRpcClientHttp
161
+
162
+ 创建 HTTP RPC 客户端。
163
+
164
+ ```js
165
+ /**
166
+ * @param {{
167
+ * url:string;
168
+ * rpcKey?:string;
169
+ * signal?:AbortSignal;
170
+ * intercept?:(res:Response)=>void;
171
+ * }} param
172
+ */
173
+ export function createRpcClientHttp(param)
174
+ ```
175
+
176
+ 参数说明:
177
+ - `url`: RPC 服务的完整 URL
178
+ - `rpcKey`: 可选的安全密钥,需与服务端一致
179
+ - `signal`: 可选的 AbortSignal,用于取消请求
180
+ - `intercept`: 可选的响应拦截函数
181
+
182
+ ## 特性
183
+
184
+ 1. **跨域支持**: 客户端可以调用不同域上的 RPC 服务
185
+ 2. **类型安全**: 通过 JSDoc 注解提供完整的类型支持
186
+ 3. **异步回调**: 支持在远程调用过程中传递回调函数
187
+ 4. **二进制数据**: 原生支持 Uint8Array 等二进制数据类型
188
+ 5. **错误处理**: 完善的错误处理和传播机制
189
+ 6. **安全验证**: 可选的 rpcKey 参数提供基本的安全验证
190
+
191
+ ## 使用场景
192
+
193
+ 1. **Web 应用**: 浏览器前端调用后端服务
194
+ 2. **微服务**: 服务间通过 HTTP 进行 RPC 调用
195
+ 3. **混合架构**: 不同技术栈的服务间通信
196
+
197
+ ## 注意事项
198
+
199
+ 1. 确保服务端正确配置 CORS(跨域资源共享)策略
200
+ 2. 大数据传输可能影响性能,考虑使用流式传输
201
+ 3. HTTP 是无状态协议,需要通过 rpcKey 或其他机制实现身份验证
202
+ 4. 服务端需要正确处理 Koa 中间件的顺序
@@ -0,0 +1,467 @@
1
+ # 其他环境使用文档
2
+
3
+ ## 概述
4
+
5
+ js-rpc 除了支持 Node.js Worker Threads、HTTP 和 WebSocket 外,还支持多种其他环境,包括 Electron、Chrome 扩展和 MessagePort 等。这些环境都基于消息传递机制实现 RPC 调用。
6
+
7
+ ## 支持的环境
8
+
9
+ ### 1. Electron
10
+
11
+ Electron 环境中可以使用 MessagePort 进行主进程和渲染进程之间的通信。
12
+
13
+ #### 服务器端 API
14
+
15
+ - [createRpcServerElectronMessagePort](../src/lib.js#L796)
16
+
17
+ #### 客户端 API
18
+
19
+ 暂无专用客户端,可使用通用 MessagePort 客户端。
20
+
21
+ ### 2. Chrome 扩展
22
+
23
+ Chrome 扩展中可以使用 chrome.runtime API 进行不同组件之间的通信。
24
+
25
+ #### 服务器端 API
26
+
27
+ - [createRpcServerChromeExtensions](../src/lib.js#L868)
28
+
29
+ #### 客户端 API
30
+
31
+ - [createRpcClientChromeExtensions](../src/lib.js#L929)
32
+
33
+ ### 3. MessagePort
34
+
35
+ 通用 MessagePort 环境,如 Web Workers 或其他支持 MessagePort API 的环境。
36
+
37
+ #### 服务器端 API
38
+
39
+ - [createRpcServerMessagePort](../src/lib.js#L760)
40
+
41
+ #### 客户端 API
42
+
43
+ - [createRpcClientMessagePort](../src/lib.js#L819)
44
+
45
+ ## 完整示例
46
+
47
+ ### 1. Electron 示例
48
+
49
+ #### 主进程代码 (main.js)
50
+
51
+ ```js
52
+ import { app, BrowserWindow, ipcRenderer } from 'electron'
53
+ import { createRpcServerElectronMessagePort } from 'js-rpc2/src/lib.js'
54
+
55
+ // 主进程服务函数
56
+ class MainProcessApi {
57
+ /**
58
+ * 获取系统信息
59
+ */
60
+ async getSystemInfo() {
61
+ return {
62
+ platform: process.platform,
63
+ arch: process.arch,
64
+ version: process.version,
65
+ uptime: process.uptime()
66
+ }
67
+ }
68
+
69
+ /**
70
+ * 执行计算密集型任务
71
+ * @param {number} n - 计算参数
72
+ */
73
+ async computeIntensiveTask(n) {
74
+ let result = 0
75
+ for (let i = 0; i < n; i++) {
76
+ result += Math.sin(i) * Math.cos(i)
77
+ }
78
+ return result
79
+ }
80
+ }
81
+
82
+ // 创建窗口时建立 RPC 连接
83
+ app.whenReady().then(() => {
84
+ const mainWindow = new BrowserWindow({
85
+ webPreferences: {
86
+ nodeIntegration: true,
87
+ contextIsolation: false
88
+ }
89
+ })
90
+
91
+ // 当需要建立 RPC 连接时
92
+ mainWindow.webContents.once('did-finish-load', () => {
93
+ // 创建 MessagePort 通道
94
+ const { port1, port2 } = new MessageChannelMain()
95
+
96
+ // 在主进程端创建 RPC 服务器
97
+ createRpcServerElectronMessagePort({
98
+ port: port1,
99
+ extension: new MainProcessApi()
100
+ })
101
+
102
+ // 将 port2 发送到渲染进程
103
+ mainWindow.webContents.postMessage('rpc-port', null, [port2])
104
+ })
105
+
106
+ mainWindow.loadFile('index.html')
107
+ })
108
+ ```
109
+
110
+ #### 渲染进程代码 (renderer.js)
111
+
112
+ ```js
113
+ import { createRpcClientMessagePort } from 'js-rpc2/src/lib.js'
114
+
115
+ // 等待主进程发送 MessagePort
116
+ window.addEventListener('message', async (event) => {
117
+ if (event.data === 'rpc-port' && event.ports.length > 0) {
118
+ const port = event.ports[0]
119
+
120
+ // 创建 RPC 客户端
121
+ /** @type{MainProcessApi} */
122
+ const rpc = createRpcClientMessagePort({
123
+ port: port
124
+ })
125
+
126
+ // 使用 RPC 调用主进程函数
127
+ try {
128
+ const systemInfo = await rpc.getSystemInfo()
129
+ console.log('System info:', systemInfo)
130
+
131
+ const result = await rpc.computeIntensiveTask(1000000)
132
+ console.log('Computation result:', result)
133
+ } catch (error) {
134
+ console.error('RPC call failed:', error)
135
+ }
136
+ }
137
+ })
138
+ ```
139
+
140
+ ### 2. Chrome 扩展示例
141
+
142
+ #### Background 脚本 (background.js)
143
+
144
+ ```js
145
+ import { createRpcServerChromeExtensions } from 'js-rpc2/src/lib.js'
146
+
147
+ // Background 脚本服务函数
148
+ class BackgroundApi {
149
+ /**
150
+ * 获取浏览器标签页信息
151
+ */
152
+ async getTabsInfo() {
153
+ const tabs = await chrome.tabs.query({})
154
+ return tabs.map(tab => ({
155
+ id: tab.id,
156
+ title: tab.title,
157
+ url: tab.url,
158
+ active: tab.active
159
+ }))
160
+ }
161
+
162
+ /**
163
+ * 创建新标签页
164
+ * @param {string} url - 标签页 URL
165
+ */
166
+ async createTab(url) {
167
+ const tab = await chrome.tabs.create({ url })
168
+ return {
169
+ id: tab.id,
170
+ url: tab.url
171
+ }
172
+ }
173
+
174
+ /**
175
+ * 带回调的长时间运行任务
176
+ * @param {(progress: string) => void} callback - 进度回调
177
+ */
178
+ async longRunningTask(callback) {
179
+ for (let i = 1; i <= 10; i++) {
180
+ await new Promise(resolve => setTimeout(resolve, 1000))
181
+ callback(`Step ${i} completed`)
182
+ }
183
+ return 'Task finished'
184
+ }
185
+ }
186
+
187
+ // 创建 RPC 服务器
188
+ createRpcServerChromeExtensions({
189
+ chrome: chrome,
190
+ key: 'background-rpc',
191
+ extension: new BackgroundApi()
192
+ })
193
+ ```
194
+
195
+ #### Popup 脚本 (popup.js)
196
+
197
+ ```js
198
+ import { createRpcClientChromeExtensions } from 'js-rpc2/src/lib.js'
199
+
200
+ // 创建 RPC 客户端
201
+ const rpc = createRpcClientChromeExtensions({
202
+ chrome: chrome,
203
+ key: 'background-rpc'
204
+ })
205
+
206
+ // DOM 元素
207
+ const tabsList = document.getElementById('tabs-list')
208
+ const urlInput = document.getElementById('url-input')
209
+ const createTabButton = document.getElementById('create-tab-button')
210
+ const progressDiv = document.getElementById('progress')
211
+
212
+ // 获取标签页信息
213
+ async function getTabsInfo() {
214
+ try {
215
+ const tabsInfo = await rpc.getTabsInfo()
216
+ tabsList.innerHTML = tabsInfo.map(tab =>
217
+ `<div class="tab-item ${tab.active ? 'active' : ''}">
218
+ <strong>${tab.title}</strong><br>
219
+ ${tab.url}
220
+ </div>`
221
+ ).join('')
222
+ } catch (error) {
223
+ console.error('Failed to get tabs info:', error)
224
+ }
225
+ }
226
+
227
+ // 创建新标签页
228
+ async function createTab() {
229
+ const url = urlInput.value
230
+ if (!url) return
231
+
232
+ try {
233
+ const result = await rpc.createTab(url)
234
+ console.log('Created tab:', result)
235
+ urlInput.value = ''
236
+ getTabsInfo() // 刷新标签页列表
237
+ } catch (error) {
238
+ console.error('Failed to create tab:', error)
239
+ }
240
+ }
241
+
242
+ // 执行长时间运行任务
243
+ async function runLongTask() {
244
+ try {
245
+ const result = await rpc.longRunningTask((progress) => {
246
+ progressDiv.textContent = progress
247
+ })
248
+ progressDiv.textContent = result
249
+ } catch (error) {
250
+ console.error('Task failed:', error)
251
+ progressDiv.textContent = 'Task failed'
252
+ }
253
+ }
254
+
255
+ // 绑定事件
256
+ createTabButton.addEventListener('click', createTab)
257
+ document.getElementById('refresh-button').addEventListener('click', getTabsInfo)
258
+ document.getElementById('run-task-button').addEventListener('click', runLongTask)
259
+
260
+ // 初始化
261
+ getTabsInfo()
262
+ ```
263
+
264
+ ### 3. Web Worker 示例
265
+
266
+ #### 主线程代码 (main.js)
267
+
268
+ ```js
269
+ import { createRpcClientMessagePort } from 'js-rpc2/src/lib.js'
270
+
271
+ // 创建 Web Worker
272
+ const worker = new Worker('./worker.js')
273
+
274
+ // 创建 RPC 客户端
275
+ /** @type{WorkerApi} */
276
+ const rpc = createRpcClientMessagePort({
277
+ port: worker
278
+ })
279
+
280
+ // 使用示例
281
+ async function example() {
282
+ try {
283
+ // 调用 Worker 中的函数
284
+ const result = await rpc.calculateFibonacci(40)
285
+ console.log('Fibonacci result:', result)
286
+
287
+ // 调用带进度更新的函数
288
+ const processDataResult = await rpc.processDataWithProgress(10000, (progress) => {
289
+ console.log(`Processing: ${progress}%`)
290
+ })
291
+ console.log('Process data result:', processDataResult)
292
+
293
+ } catch (error) {
294
+ console.error('RPC call failed:', error)
295
+ }
296
+ }
297
+
298
+ example()
299
+ ```
300
+
301
+ #### Worker 线程代码 (worker.js)
302
+
303
+ ```js
304
+ import { createRpcServerMessagePort } from 'js-rpc2/src/lib.js'
305
+
306
+ // Worker 中的服务函数
307
+ class WorkerApi {
308
+ /**
309
+ * 计算斐波那契数
310
+ * @param {number} n - 数列位置
311
+ */
312
+ async calculateFibonacci(n) {
313
+ if (n <= 1) return n
314
+ let a = 0, b = 1
315
+ for (let i = 2; i <= n; i++) {
316
+ const temp = a + b
317
+ a = b
318
+ b = temp
319
+ }
320
+ return b
321
+ }
322
+
323
+ /**
324
+ * 带进度更新的数据处理
325
+ * @param {number} dataSize - 数据大小
326
+ * @param {(progress: number) => void} progressCallback - 进度回调
327
+ */
328
+ async processDataWithProgress(dataSize, progressCallback) {
329
+ const data = new Array(dataSize).fill(0).map(() => Math.random())
330
+
331
+ let processed = 0
332
+ const step = Math.max(1, Math.floor(dataSize / 100)) // 每1%报告一次进度
333
+
334
+ for (let i = 0; i < dataSize; i++) {
335
+ // 模拟处理过程
336
+ data[i] = Math.pow(data[i], 2) + Math.sqrt(data[i])
337
+
338
+ processed++
339
+ if (processed % step === 0 || processed === dataSize) {
340
+ const progress = Math.round((processed / dataSize) * 100)
341
+ progressCallback(progress)
342
+ }
343
+ }
344
+
345
+ return {
346
+ processedItems: processed,
347
+ average: data.reduce((sum, val) => sum + val, 0) / dataSize
348
+ }
349
+ }
350
+ }
351
+
352
+ // 创建 RPC 服务器
353
+ createRpcServerMessagePort({
354
+ port: self,
355
+ extension: new WorkerApi()
356
+ })
357
+ ```
358
+
359
+ ## API 说明
360
+
361
+ ### Electron
362
+
363
+ #### createRpcServerElectronMessagePort
364
+
365
+ 在 Electron 主进程中创建 RPC 服务器。
366
+
367
+ ```js
368
+ /**
369
+ * @param {{
370
+ * port:Electron.MessagePortMain;
371
+ * rpcKey:string;
372
+ * extension: object;
373
+ * logger?:(msg:string)=>void;
374
+ * }} param
375
+ */
376
+ export function createRpcServerElectronMessagePort(param)
377
+ ```
378
+
379
+ ### Chrome 扩展
380
+
381
+ #### createRpcServerChromeExtensions
382
+
383
+ 在 Chrome 扩展中创建 RPC 服务器。
384
+
385
+ ```js
386
+ /**
387
+ * @param {{
388
+ * chrome:Chrome;
389
+ * key: string;
390
+ * extension: ExtensionApi<Chrome.runtime.MessageSender>
391
+ * logger?:(msg:string)=>void;
392
+ * }} param
393
+ */
394
+ export function createRpcServerChromeExtensions(param)
395
+ ```
396
+
397
+ #### createRpcClientChromeExtensions
398
+
399
+ 在 Chrome 扩展中创建 RPC 客户端。
400
+
401
+ ```js
402
+ /**
403
+ * @param {{
404
+ * chrome:Chrome;
405
+ * key:string;
406
+ * tabId?: number;
407
+ * }} param
408
+ */
409
+ export function createRpcClientChromeExtensions(param)
410
+ ```
411
+
412
+ ### MessagePort
413
+
414
+ #### createRpcServerMessagePort
415
+
416
+ 在任意支持 MessagePort 的环境中创建 RPC 服务器。
417
+
418
+ ```js
419
+ /**
420
+ * @param {{
421
+ * port:MessagePort;
422
+ * rpcKey:string;
423
+ * extension: object;
424
+ * logger?:(msg:string)=>void;
425
+ * }} param
426
+ */
427
+ export function createRpcServerMessagePort(param)
428
+ ```
429
+
430
+ #### createRpcClientMessagePort
431
+
432
+ 在任意支持 MessagePort 的环境中创建 RPC 客户端。
433
+
434
+ ```js
435
+ /**
436
+ * @param {{
437
+ * port:MessagePort;
438
+ * rpcKey:string;
439
+ * }} param
440
+ */
441
+ export function createRpcClientMessagePort(param)
442
+ ```
443
+
444
+ ## 特性
445
+
446
+ 1. **跨环境兼容**: 支持多种基于消息传递的环境
447
+ 2. **类型安全**: 通过 JSDoc 注解提供完整的类型支持
448
+ 3. **异步回调**: 支持在远程调用过程中传递回调函数
449
+ 4. **二进制数据**: 原生支持 Uint8Array 等二进制数据类型
450
+ 5. **错误处理**: 完善的错误处理和传播机制
451
+ 6. **灵活部署**: 可根据不同环境特点灵活部署
452
+
453
+ ## 使用场景
454
+
455
+ 1. **Electron 应用**: 主进程与渲染进程通信
456
+ 2. **Chrome 扩展**: 不同组件间通信(popup、content scripts、background)
457
+ 3. **Web Workers**: 主线程与 Worker 线程通信
458
+ 4. **Shared Workers**: 多页面共享 Worker
459
+ 5. **iframe 通信**: 不同源页面间通信
460
+
461
+ ## 注意事项
462
+
463
+ 1. 不同环境的 MessagePort 实现可能略有差异
464
+ 2. Chrome 扩展需要正确配置 manifest 权限
465
+ 3. Electron 环境需要正确设置 webPreferences
466
+ 4. 注意处理跨域和安全限制
467
+ 5. 合理管理 MessagePort 的生命周期
@@ -0,0 +1,223 @@
1
+ # WebSocket 使用文档
2
+
3
+ ## 概述
4
+
5
+ js-rpc 支持通过 WebSocket 协议进行 RPC 调用,提供了比 HTTP 更高效的双向通信能力。WebSocket 传输适用于需要实时通信、频繁交互或长时间连接的应用场景。
6
+
7
+ ## 核心组件
8
+
9
+ ### 服务器端
10
+
11
+ 1. [createRpcServerWebSocket](../src/server.js#L37) - 创建 WebSocket RPC 服务器
12
+ 2. [createRpcServerHelper](../src/lib.js#L467) - 创建 RPC 服务器助手(内部使用)
13
+
14
+ ### 客户端
15
+
16
+ 1. [createRpcClientWebSocket](../src/lib.js#L656) - 创建 WebSocket RPC 客户端
17
+
18
+ ## 完整示例
19
+
20
+ ### 1. 服务器端代码 (server.js)
21
+
22
+ ```js
23
+ import { createServer } from 'http'
24
+ import { WebSocketServer } from 'ws'
25
+ import { createRpcServerWebSocket } from 'js-rpc2/src/server.js'
26
+
27
+ // 创建 HTTP 服务器
28
+ const server = createServer()
29
+
30
+ // 创建 WebSocket 服务器
31
+ const wss = new WebSocketServer({ server })
32
+
33
+ // 定义可远程调用的函数
34
+ class RpcApi {
35
+ /**
36
+ * 示例方法:带回调的异步函数
37
+ * @param {string} name - 名称参数
38
+ * @param {(data: string) => void} update - 回调函数,用于报告进度
39
+ */
40
+ async hello(name, update) {
41
+ for (let i = 0; i < 5; i++) {
42
+ // 调用回调函数报告进度
43
+ update(`progress ${i}`)
44
+ await new Promise((resolve) => setTimeout(resolve, 500))
45
+ }
46
+ return `Hello, ${name}!`
47
+ }
48
+
49
+ /**
50
+ * 示例方法:处理二进制数据
51
+ * @param {Uint8Array} buffer - 二进制数据
52
+ */
53
+ async processBuffer(buffer) {
54
+ // 模拟处理过程
55
+ await new Promise((resolve) => setTimeout(resolve, 1000))
56
+ // 返回处理后的数据
57
+ return new Uint8Array(buffer.map(b => b * 2))
58
+ }
59
+
60
+ /**
61
+ * 示例方法:实时数据推送
62
+ * @param {(data: object) => void} callback - 数据推送回调
63
+ */
64
+ async subscribeToData(callback) {
65
+ // 模拟实时数据推送
66
+ for (let i = 0; i < 10; i++) {
67
+ await new Promise((resolve) => setTimeout(resolve, 1000))
68
+ callback({
69
+ timestamp: Date.now(),
70
+ value: Math.random(),
71
+ index: i
72
+ })
73
+ }
74
+ return 'Subscription ended'
75
+ }
76
+ }
77
+
78
+ // 创建 API 实例
79
+ const extension = new RpcApi()
80
+
81
+ // 创建 RPC 服务器
82
+ createRpcServerWebSocket({
83
+ path: '/rpc', // RPC 服务路径
84
+ wss: wss, // WebSocket 服务器实例
85
+ extension: extension, // 可调用的函数集合
86
+ rpcKey: 'optional-secret-key' // 可选的安全密钥
87
+ })
88
+
89
+ // 启动服务器
90
+ server.listen(9000, () => {
91
+ console.log('WebSocket RPC server listening on port 9000')
92
+ })
93
+ ```
94
+
95
+ ### 2. 客户端代码 (client.js)
96
+
97
+ ```js
98
+ import { createRpcClientWebSocket } from 'js-rpc2/src/client.js'
99
+
100
+ // 创建 AbortController 用于控制连接
101
+ const ac = new AbortController()
102
+
103
+ // 创建 RPC 客户端
104
+ /** @type{RpcApi} */
105
+ const rpc = createRpcClientWebSocket({
106
+ url: 'ws://127.0.0.1:9000/rpc', // WebSocket RPC 服务地址
107
+ rpcKey: 'optional-secret-key', // 可选的安全密钥(需与服务端一致)
108
+ signal: ac.signal // 用于控制连接的信号
109
+ })
110
+
111
+ // 使用示例
112
+ async function example() {
113
+ try {
114
+ // 调用带回调的异步函数
115
+ console.log('Calling hello with progress updates...')
116
+ const result = await rpc.hello('WebSocket User', (progress) => {
117
+ console.log('Progress update:', progress)
118
+ })
119
+ console.log('Hello result:', result)
120
+
121
+ // 调用处理二进制数据的函数
122
+ console.log('Processing binary data...')
123
+ const buffer = new Uint8Array([1, 2, 3, 4, 5])
124
+ const processedBuffer = await rpc.processBuffer(buffer)
125
+ console.log('Processed buffer:', processedBuffer)
126
+
127
+ // 订阅实时数据
128
+ console.log('Subscribing to real-time data...')
129
+ const subscriptionResult = await rpc.subscribeToData((data) => {
130
+ console.log('Real-time data:', data)
131
+ })
132
+ console.log('Subscription result:', subscriptionResult)
133
+
134
+ } catch (error) {
135
+ console.error('RPC call failed:', error)
136
+ }
137
+ }
138
+
139
+ // 执行示例
140
+ example()
141
+
142
+ // 在需要时断开连接
143
+ // ac.abort()
144
+ ```
145
+
146
+ ## API 说明
147
+
148
+ ### 服务器端 API
149
+
150
+ #### createRpcServerWebSocket
151
+
152
+ 创建 WebSocket RPC 服务器。
153
+
154
+ ```js
155
+ /**
156
+ * @param {{
157
+ * path: string;
158
+ * wss: WebSocketServer;
159
+ * rpcKey:string;
160
+ * extension: {asyncLocalStorage:AsyncLocalStorage<IncomingMessage>;};
161
+ * logger?:(msg:string)=>void;
162
+ * }} param
163
+ */
164
+ export function createRpcServerWebSocket(param)
165
+ ```
166
+
167
+ 参数说明:
168
+ - `path`: RPC 服务的路径,用于区分不同的服务
169
+ - `wss`: WebSocketServer 实例
170
+ - `rpcKey`: 可选的安全密钥,用于验证客户端连接
171
+ - `extension`: 包含可远程调用函数的对象
172
+ - `logger`: 可选的日志函数
173
+
174
+ ### 客户端 API
175
+
176
+ #### createRpcClientWebSocket
177
+
178
+ 创建 WebSocket RPC 客户端。
179
+
180
+ ```js
181
+ /**
182
+ * @param {{
183
+ * url:string;
184
+ * rpcKey:string;
185
+ * signal:AbortSignal;
186
+ * intercept?:(e:Event)=>void;
187
+ * }} param
188
+ */
189
+ export function createRpcClientWebSocket(param)
190
+ ```
191
+
192
+ 参数说明:
193
+ - `url`: WebSocket RPC 服务的完整 URL
194
+ - `rpcKey`: 可选的安全密钥,需与服务端一致
195
+ - `signal`: AbortSignal,用于控制连接的生命周期
196
+ - `intercept`: 可选的事件拦截函数
197
+
198
+ ## 特性
199
+
200
+ 1. **双向通信**: WebSocket 提供全双工通信,服务器也可以主动向客户端推送数据
201
+ 2. **实时性**: 低延迟通信,适用于实时应用
202
+ 3. **连接保持**: 长连接减少重复握手开销
203
+ 4. **类型安全**: 通过 JSDoc 注解提供完整的类型支持
204
+ 5. **异步回调**: 支持在远程调用过程中传递回调函数
205
+ 6. **二进制数据**: 原生支持 Uint8Array 等二进制数据类型
206
+ 7. **自动重连**: 客户端具备自动重连机制
207
+ 8. **错误处理**: 完善的错误处理和传播机制
208
+
209
+ ## 使用场景
210
+
211
+ 1. **实时应用**: 聊天应用、实时游戏、协作编辑等
212
+ 2. **数据推送**: 实时数据监控、股票价格更新等
213
+ 3. **频繁交互**: 需要频繁通信的应用,避免 HTTP 请求的开销
214
+ 4. **长时间连接**: 需要保持连接状态的应用
215
+
216
+ ## 注意事项
217
+
218
+ 1. WebSocket 连接需要正确处理断开和重连逻辑
219
+ 2. 服务器需要合理管理并发连接数
220
+ 3. 注意处理网络异常和超时情况
221
+ 4. 对于大规模部署,考虑使用负载均衡器
222
+ 5. 安全方面,确保使用 WSS(WebSocket Secure)进行加密传输
223
+ 6. 在防火墙环境中,确保 WebSocket 端口未被阻止
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-rpc2",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "description": "js web websocket http rpc",
5
5
  "main": "index.js",
6
6
  "type": "module",