likes
comments
collection
share

Electron 应用如何和其他应用通信

作者站长头像
站长
· 阅读数 18

不知道大家在开发 electron 应用时,是否遇到过这样的需求:目前,公司有两个桌面应用,希望在启动一个应用时,尝试询问另一个应用的登录信息,实现自动登录。

Electron 应用如何和其他应用通信

类似这样的功能,我们如何实现?

习惯了 web 开发的同学可能立马就能想到,让应用启动一个本地 web 服务,通过 http 协议来通信,这是一个不错的方法。当然,今天我要讲的肯定不是这么简单。我要给大家讲的是如何通过 NamedPipe(命名管道)Unix domain socket 来进行进程间通信, 这两种都是双工通信,前者是windows 的,后者是类 unix 上的。由于 Node 基于 libv 实现了跨平台,抹平了两个平台实现的差异,所以我们用 node 来实现就简单了许多。

Node: Net 模块

想要进行进程间通信,两个应用必然要有一个当担服务端(App.A),一个担当客户端(App.B)的角色。这里,我们借助 Node 的 Net 模块就可以实现进程间通信。

创建服务

const os = require("os");
const net = require("net");
const path = require("path")

const server = net.createServer((c) => {
    // ...
});

// named pipe
sever.listen("\\\\.\\pipe\\App.A")
// unix socket, 以 mac 为例
server.listen(path.join(os.homedir(), 'Library/GroupContainer/.App.A.d/node.sock'))
// tcp/ip port
server.listen(10086);

连接服务

const net = require("net");
// 以 named pipe 为例
const handle = net.connect("\\\\.\\pipe\\App.A")
handle.on("error", () => {
    // 连接失败
});

handle.on("ready", () => {
    // 连接成功,后续可以使用 handle 句柄向通道内发送数据
});

handle.on("close", () => {
    // 连接断开
})

创建 api,响应请求

const net = require("net")

net.createServer(socket => {
    socket.on("error", (e) => {
        // connect error
    })
    // client connected
    socket.on("data", (buffer) => { 
        const command = buffer[0];
        const dataLength  = buffer.readUInt32LE(1); // 4 bytes
        const dataStart = 5; // 1 byte + 4 bytes
        const dataEnd = dataStart + dataLength;
        const dataBuffer = buffer.slice(dataStart, dataEnd);
        if (dataBuffer.byteLength !== dataLength) {
            throw new Error("invalid data")
        }
        const json = dataBuffer.toString("utf-8"); // json
        const data = JSON.parse(json)
        
        // 处理请求数据 data
        switch(command){
            case 1: // get user login info
                // ....
                socket.write('xxxxx\n') // 写入数据
                socket.end() // 发送数据
                break;
            case 2: // 其他 api 实现
            default: 
                break;
        }
        
    })
})

上面的例子中可以看到,我对接受的数据做了一些格式上的约定,第1个字节表示 commandType, 第2-5 占用4个字节的 int32 数值用来表示后续数据实体的字节长度,简单校验数据的合法性,并截取需要的数据。这是使用 Buffer 传输数据的常用技巧,大家可以留意一下。

“客户端”如何请求“服务端”

const net = require("net");
const path = require("path");
const os = require("os");

const address = path.join(
  os.homedir(),
  "Library/Group Containers/.App.A.d/node.sock"
);

const handle = net.connect(address);

handle.on("error", (e) => {
  // connect error
  console.log(e);
});

handle.on("ready", () => {
  //
  const command = Buffer.from([1]);

  const data = {
    name: "张三",
  };
  const json = JSON.stringify(data);

  const dataBuffer = Buffer.from(json, "utf-8");

  const dataLength = dataBuffer.byteLength;

  const dataLengthBuffer = Buffer.alloc(4);
  dataLengthBuffer.writeUInt32LE(dataLength, 0);

  const buffer = Buffer.concat([command, dataLengthBuffer, dataBuffer]);

  handle.once("data", (data) => {
    console.log("response =>>>", data); // 接收到应用A返回的数据
    handle.destroy();
  });

  handle.write(buffer);
  handle.end();
});

这样一个简单的进程间通信就完成了。

这个 demo 我会补充完整后放到 github 上供大家参考。

相关资料

进程间通信方式