基于node的net模块实现electron的管道通信

453 阅读2分钟

前言

electron分了主进程和渲染进程, 如果在主进程中执行的时间过长会导致页面卡顿,因为主进程管理者渲染器进程,为了优化,做了很多的一个方案:

1. 再开一个BrowserWindow 窗口来处理这些逻辑,设置这个窗口隐藏,然后通过electron内置的ipc进行消息的通讯
2. 利用webWorker来处理这些复杂的逻辑
3. 使用golang等处理业务,打包成exe,使用node的child_process启动exe,通过socket处理
4. 通过的node的fork启动一个node服务来处理逻辑 
...

像1、3、4这种本质上是要开多一个进程去处理,那么进程之间的通信就很重要了,比如socket,ipcRender,ipcMain。

我有个项目就用了socket做了通信,不过用socket做通信可能会遇到端口占用的情况,就是你先跑了socket,然后端口被其他应用占掉,这时注意重新拉起。

命名管道通信

回归正题,现在做一个使用net模块实现electron主进程和node进程的命名管道通信,功能比较简单,就是获取文件夹下所有的文件和文件夹。

这边读取文件夹下所有文件和文件夹作为Server,electron主进程是client

fileDirRead.js:

const net = require("net");
const pipeFile =
  process.platform === "win32" ? "\\\\.\\pipe\\rfpip" : "/tmp/rfpip.sock";
const fs = require("fs");
const path = require("path");
const readDir = (dirPath, arr) => {
  const dirs = [];
  const files = fs.readdirSync(dirPath);
  files.forEach((file) => {
    const localPath = path.join(dirPath, file);
    if (fs.statSync(localPath).isDirectory()) {
      dirs.push(localPath);
    }
    arr.push(localPath);
  });
  while (dirs.length > 0) {
    const dir = dirs.shift();
    const parent = dir;
    const files = fs.readdirSync(dir);
    files.forEach((file) => {
      const localPath = path.join(parent, file);
      if (fs.statSync(localPath).isDirectory()) {
        dirs.push(localPath);
      }
      arr.push(localPath);
    });
  }
};
const server = net.createServer((connection) => {
  connection.on("close", () => console.log("stop"));
  connection.on("data", (data) => {
    const arr =[];
    readDir(data.toString(),arr);
    connection.write(JSON.stringify(arr));
  });
  connection.on("error", (err) => console.error(err.message));
});

try {
  fs.unlinkSync(pipeFile);
} catch (error) {
  console.log("error:", error.message);
}
server.listen(pipeFile);

main.js:

const { app, BrowserWindow, ipcMain } = require("electron");
const path = require("path");
const net = require("net");
let pipClient = null;
let client = null;
let mainWindow = null;
const { fork } = require("child_process");
...
const startNodeProcess = () => {
  client = fork(path.resolve("./fileDirRead.js"));
  client.on("close", () => {
    console.log("客户端关闭")
    client = null;
  });
};
const pipConnect = () => {
  if (client) {
    const pipeFile =
    process.platform === "win32" ? "\\\\.\\pipe\\rfpip" : "/tmp/rfpip.sock";
    setTimeout(() =>{
      pipClient = net.connect(pipeFile);
      pipClient.on("connect", () => console.log("连接"));
      pipClient.on("data", (data) => {
        if (mainWindow) {
          mainWindow.webContents.send("getDir",data.toString())
        }
      });
      pipClient.on("close", () => console.log("关闭"));
    },400)
  }
};

app.whenReady().then(() => {
  createWindow();
  // 启动
  startNodeProcess();
  pipConnect();
  ipcMain.on("readdir", (event, dirPath) => {
    if (pipClient) {
      pipClient.write(dirPath)
    }
  });
});

renderer.js:

const ipcRenderer = window.require("electron").ipcRenderer
const button = document.getElementById("button");
button.addEventListener('click',() => {
     input = document.getElementById("text");
     const data=input.value;
     console.log("发送",data)
     ipcRenderer.send("readdir",data);
 })
ipcRenderer.on("getDir",(event,data)=>{
    const frame = document.createDocumentFragment();
    const lis = JSON.parse(data);
    lis.forEach(item=>{
        const li =document.createElement("li");
        li.innerText = item;
        frame.appendChild(li);
    })
    const ul = document.getElementById("ul");
    ul.innerHTML = "";
    ul.appendChild(frame);
})

index.html:

<!DOCTYPE html>
<html>
  <head>
    ....
  </head>
  <body>
    <h1>读取文件</h1>
    <input type="text" id="text" />
    <button id="button">读取</button>
    <ul id="ul">
       
    </ul>
    <!-- You can also require other files to run in this process -->
    <script src="./renderer.js"></script>
  </body>
</html>

效果:

1664359377008.png

总结

大概就是这样子了,当然就有点坑,比如乱码什么的,