0.6.7 • Published 6 months ago

cpcall v0.6.7

Weekly downloads
-
License
MIT
Repository
github
Last release
6 months ago

Features

  • 远程调用可操作远程代理对象,与原生 JavaScript 调用语法几乎无差别
  • 可使用 ECMA 装饰器定义服务。 查看装饰器的使用
  • 与协议无关,可用于基于 TCP、IPC、WebSocket 等
  • 类型安全
  • 双端远程调用
  • 数据传输默认采用 JBOD 二进制编码。相比 JSON,有如下优势:
    • 更多的数据类型。如 bigint、Set、Map、RegExp、Error、UInt8Array 等(查看支持的数据类型),这意味着在调用远程方法时,你可以直接传递这些参数,而无需进行转换
    • 更小的数据大小。对于常见场景,编码后大小 大概是 JSON 的 70%,
  • 无需定义数据结构,非常适合动态类型语言

Usage

Expose globalThis

下面的示例中,我们将在两端暴露全局对象,并在客户端调用服务端的 console.log() 方法后关闭连接

Node

tcp server

import net from "node:net";
import { createSocketCpc } from "cpcall";
const server = new net.Server(async function (socket) {
  const cpc = createSocketCpc(socket);
  cpc.exposeObject(globalThis);
  const remote = cpc.genCaller();
  remote.console.log("Hi, I am Server");
});
server.listen(8888);

websocket server

import { WebSocketServer } from "ws";
import http from "node:http";
import { createWebSocketCpcOnOpen } from "cpcall";
const server = new http.Server();
const wsServer = new WebSocketServer({ server });
wsServer.on("connection", async (ws) => {
  const cpc = await createWebSocketCpcOnOpen(ws);
  cpc.exposeObject(globalThis);
  const remote = cpc.genCaller();
  remote.console.log("Hi, I am Server");
});
server.listen(8887);

tcp client

import { connect, Socket } from "node:net";
import { createSocketCpc } from "cpcall";

const socket = connect(8888);
socket.on("connect", async () => {
  const cpc = createSocketCpc(socket);
  const remote = cpc.genCaller<typeof globalThis>();
  remote.console.log("Hi, I am Server");
  await cpc.close();
});

Deno

tcp server

import { createWebStreamCpc } from "jsr:@asn/cpcall";

const server = Deno.listen({ port: 8888 });
for await (const conn of server) {
  const cpc = createWebStreamCpc(conn);
  cpc.exposeObject(globalThis);
  const remote = cpc.genCaller();
  remote.console.log("Hi, I am Server");
}

http server

import { createWebSocketCpcOnOpen } from "jsr:@asn/cpcall";

Deno.serve({ port: 8887 }, function (req, res): Response {
  const upgrade = req.headers.get("upgrade") || "";
  if (upgrade.toLowerCase() != "websocket") {
    return new Response("hi");
  }
  const { response, socket } = Deno.upgradeWebSocket(req);
  createWebSocketCpcOnOpen(socket).then((cpc): void => {
    cpc.exposeObject(globalThis);
    const remote = cpc.genCaller();
    remote.console.log("Hi, I am Server");
  }, console.error);
  return response;
});

tcp client

import { createWebStreamCpc } from "jsr:@asn/cpcall";

const conn = await Deno.connect({ port: 8888 });
const cpc = createWebStreamCpc(conn);
cpc.exposeObject(globalThis);

const remote = cpc.genCaller<typeof globalThis>();
remote.console.log("Hi, I am Server");
await cpc.close();

Browser

import { createWebSocketCpcOnOpen } from "https://esm.sh/cpcall";

const ws = new WebSocket("ws://127.0.0.1:8887");
const cpc = await createWebSocketCpcOnOpen(ws);
cpc.exposeObject(globalThis);
const remote = cpc.genCaller<typeof globalThis>();
remote.console.log("Hi, I am Server");

await cpc.close();

Expose object

下面的示例中,展示了代理对象的调用

server.ts

class Service {
  add(a: number, b: number) {
    return a + b;
  }
  multiType(...args: any[]) {
    return args.length;
  }
  getPromise(time: number): Promise<number> {
    return new Promise<number>((resolve) => setTimeout(() => resolve(time), time));
  }
  throwError() {
    throw new Error("throw an error");
  }
  obj = {
    method0() {
      return 0;
    },
    lv1: {
      lv2: {
        method2() {
          return 2;
        },
      },
    },
  };
}

cpc.exposeObject(new Service());

export type { Service };

client.ts

import type { Service } from "./server.ts";

await cpc1.call("add", 1, 2);
cpc1.exec("add", 1, 2); // No need to retrieve the return value

const service = cpc1.genCaller<Service>(); // Use proxy objects to obtain complete type prompts

await service.add(1, 2);
await service.getPromise(100);
await service.throwError().catch((e) => {
  console.log(e); // Error: throw an error
});

const args = [
  {
    number: 1,
    bigint: 2n,
    string: "text",
    regExp: /\d+/,
    boolean: true,
    symbol: Symbol("cpcall"),
  },
  undefined,
  null,
  new Uint8Array(10),
  new Error("ha ha"),
  new Set([1, 2, 3]),
  new Map([["str", 1]]),
];
await service.multiType(...args); // Supports many data types

await service.obj.method0();
await service.obj.lv1.lv2.method2(); // Deep call

更多示例

装饰器的使用\ 自定义数据帧序列化(文档待补充)\ 通过 CpCall 类 实现一个基于 http 的 CpCall(文档待补充)\ 更多示例

概念

远程过程调用(RPC)

tRpcgRpcsocket.io 的区别

与 tRpc、gRpc 最直接的一个区别是,tRpc、gRpc 都是通过客户端主动发起请求(调用),服务端进行响应,的模式,他们只能单向发起调用。而 cpcall,可以进行双向相互调用

名称基于协议调用方向
tRpchttp单向调用
gRpchttp2单向调用
cpcall双向流(与协议无关)双向调用

socket.io 是一个基于 WebSocket 的库,可实现双端之间的双向实时通信,它提供了单播、多播等行为。使用它主要用到发布订阅模式。 而 cpcall,是一个端到端双向调用的 RPC 库。cpcall 与 socket.io 本质上不属于同一类型的库,但在 WebSocket 协议下,他们都能达到相似的行为。

其他

CPCALL 数据帧协议

0.6.7

6 months ago

0.6.6

7 months ago

0.6.5

7 months ago

0.6.4

11 months ago

0.6.3

11 months ago

0.6.2

12 months ago

0.6.1

12 months ago

0.6.0

12 months ago

0.5.2

1 year ago

0.5.1

1 year ago

0.4.9

1 year ago

0.5.0

1 year ago

0.4.8

1 year ago

0.4.7

1 year ago

0.4.6

1 year ago

0.4.5

1 year ago

0.4.4

1 year ago

0.4.1

1 year ago

0.4.3

1 year ago

0.4.2

1 year ago

0.4.0

1 year ago

0.3.3

1 year ago

0.3.2

1 year ago

0.3.1

1 year ago

0.3.0

1 year ago

0.2.9

1 year ago

0.2.7

1 year ago

0.2.6

1 year ago

0.2.8

1 year ago

0.2.3

1 year ago

0.2.5

1 year ago

0.2.4

1 year ago

0.2.1

1 year ago

0.2.2

1 year ago

0.2.0

1 year ago

0.1.1

1 year ago

0.1.0

1 year ago

0.0.6

1 year ago

0.0.5

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago

0.0.1-6

2 years ago

0.0.1-5

2 years ago

0.0.1-4

2 years ago

0.0.1-3

2 years ago

0.0.1-2

2 years ago

0.0.1-1

2 years ago

0.0.1-0

2 years ago