Theme NexT works best with JavaScript enabled
0%

基于Electron的远程控制软件

^ _ ^

技术关键点

  1. 怎么捕获画面? ==> Electron desktopCapture
  2. 怎么完成用户间连接、画面+指令传输? ==> WebRTC
  3. 怎么相应控制指令? ==> robotjs

目录架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|------- package.json
|------- app
| |--- common
| | |--- ipc-channel.js
| | |--- util.js
| |--- main
| | |--- index.js
| | |--- ipc.js
| | |--- windows
| | | |--- control.js
| | | |--- main.js
| |--- renderer
| | |--- pages
| | | |--- control
| | | | |--- index.html
| | | |--- main
| | |--- src
|------- resource
|------- release
|------- dist

搭建项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
mkdir remote-control
cd remote-control
npm init -y
mkdir app
cd app
mkdir main
mkdir renderer
cd renderer
mkdir src
mkdir pages
cd pages
mkdir control

# 进入src目录
cd ../src
npx create-react-app main --use-npm
# 下载完成后进入main目录
cd main
# 启动程序
npm start
# 打开localhost:3000可以看到应用创建成功

# 进入app下的main目录
cd ../../../main
# 添加index.js文件

安装依赖

进入项目根目录

electron依赖

1
cnpm install electron --save-dev

electron-is-dev依赖

用于生产环境和开发环境的判断

1
cnpm install electron-is-dev --save

index.js文件

在目录 app/main下

1
2
3
4
5
6
7
8
9
10
11
12
const { app, BrowserWindow } = require('electron')

app.on('ready', () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
win.loadURL("http://localhost:3000")
})

修改项目根目录下的package.json文件

1
2
3
4
5
"main": "app/main/index.js",
"scripts": {
"start:main": "electron",
"start:render": "cd app/renderer/src/main && npm start"
}

启动命令:

1
2
npm run start:render
npm run start:main

输入两次命令可能会比较麻烦,想要一步操作需要进行一些配置。

  1. 首先是在项目根目录下安装 concurrentlywait-on 模块
    1
    2
    cnpm install concurrently --save-dev
    cnpm install wait-on --save-dev
  2. 在根目录的 package.json 下的 scripts 中增加一条 start 命令
    1
    "start": "concurrently \"npm run start:render\" \"wait-on http://localhost:3000 && npm run start:main\"",
  3. 终端输入命令
    1
    npm start

App.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import logo from './logo.svg';
import './App.css';
import React, {useState, useEffect} from 'react';
// import {ipcRenderer} from 'electron';
const {ipcRenderer} = window.require('electron')

function App() {
const [remoteCode, setRemoteCode] = useState('')
const [localCode, setLocalCode] = useState('')
const [controlText, setControlText] = useState('')
const login = async () => {
let code = await ipcRenderer.invoke('login')
setLocalCode(code)
}
useEffect(() => {
login()
ipcRenderer.on('control-state-change', handleControlState)
return () => {
ipcRenderer.removeListener('control-state-change', handleControlState)
}
}, [])

const startControl = (remoteCode) => {
ipcRenderer.send('control', remoteCode)
}

const handleControlState = (e, name, type) => {
let text = ''
if(type == 1){
text = `正在远程控制${name}`
}
else if(type == 2){
text = `被${name}控制中`
}
setControlText(text)
}

return (
<div className="App">
{
controlText === '' ? <>
<div>你的控制码 {localCode}</div>
<input type="text" value={remoteCode} onChange={e => setRemoteCode(e.target.value)}/>
<button onClick={() => startControl(remoteCode)}>确认</button>
</> : <div>{controlText}</div>
}
</div>
);
}

export default App;

ipc.js

在目录 app/main下

1
2
3
4
5
6
7
8
9
const {ipcMain} = require('electron')

module.exports = function(){
ipcMain.handle('login', async () => {
// 先 mock,返回一个code
let code = Math.floor(Math.random() * (999999 - 100000)) + 100000
return code
})
}

main.js

在 app/main/windows 目录下

1
2