Skip to content

通信

现代桌面GUI应用很少有孤立存在的,它们往往都需要和其他应用通信,比如和Web服务器上的服务进行通信、和系统内其他应用进行通信等。
有了通信能力我们开发的应用程序才能接入其他应用的数据、把自己的数据保存在更安全的地方,只有依靠通信能力才能使你的应用程序成为互联网大家庭中的一员。

与 Web 服务器通信

禁用同源策略以实现跨域

桌面GUI应用最常见的通信方式还是使用HTTP协议与Web服务进行通信。
我们可以使用Node.js提供的http或https模块来完成这些通信任务,示例代码如下:

1
2
3
4
5
6
7
let https = require("https");
let url = "https:// www.cnblogs.com/aggsite/AggStats";
https.get(url, res => {
    let html = "";
    res.on("data", data => (html += data));
    res.on("end", () => console.log(html));
});

在开发实际应用的过程中,大部分通信请求都是在渲染进程中发生的,开发者完全可以使用原生的AJAX技术去请求Web服务,何必要再借助Node.js呢?
这主要是因为程序中渲染进程的逻辑都是写在本地文件里的,本地文件与Web服务肯定不在同一个域下,所以想在渲染进程里直接使用AJAX访问Web服务就会碰到跨域的问题。
现在我们尝试用如下代码请求一个Web服务:

1
2
3
4
5
// 这是一段“原始”的AJAX代码,现实中你可能会用axios之类的库来访问Web服务
let xhr = new XMLHttpRequest();
xhr.open("GET", "https:// www.cnblogs.com/aggsite/AggStats");
xhr.onload = () => console.log(xhr.responseText);
xhr.send();

你会发现开发者工具报出了如下错误:

1
Access to XMLHttpRequest at 'https:// www.cnblogs.com/aggsite/AggStats' from origin 'http:// localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

这就是我们在Web开发中因为浏览器的“同源策略”导致的跨域问题。

扩展: 
所谓“同源”是指如果两个页面的协议、端口和主机都相同,则两个页面具有相同的源,比如:http://store.company.com/a.htmlhttp://store.company.com/b.html 就是同源的,但它们和 https://store.company.com/c.html 是不同源的,因为 HTTP 和 HTTPS 协议不同。
另外二级域名不同也被认为是不同源的。

“同源策略”是浏览器的一个安全功能,它规定不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。
只有同源的脚本才具备读写 Cookie、session、AJAX 等的操作权限。

如果你正在做Web开发,就不得不想各种办法来绕开浏览器的同源策略,以实现你的跨域请求的目的。
但现在不一样了,“浏览器”掌握在我们自己手里,我们只需要简单地设置一个创建窗口的参数,就可以禁用Electron的同源策略,代码如下:

1
2
3
4
5
6
7
let win = new BrowserWindow({
    width: 800,height: 600,
    webPreferences: {
        nodeIntegration: true,
        webSecurity: false,        // 此参数禁用当前窗口的同源策略
    }
})

关闭webSecurity开关后,再执行上面的代码,就可以正确地得到Web服务响应的内容了。

相似的,webPreferences 配置项下还有一个 allowRunningInsecureContent 参数,如果把它设置为 true,那么你就可以在一个 HTTPS 页面内访问 HTTP 协议提供的服务了,这在默认情况下也是不被允许的。
当开发者把 webSecurity 设置为 false 时,allowRunningInsecureContent 也会被自动设置为 true

Node.js 访问 HTTP 服务的不足

如果你需要在主进程中访问Web服务,那么也不推荐你使用Node.js提供的http或https模块,因为这可能会为你带来一些额外的工作。
现在假设你需要访问的Web服务的地址是动态的,有可能是基于HTTP协议,也有可能是基于HTTPS协议的,
想完成这样的请求,你势必要先确定协议,再确定该导入http模块还是https模块。
代码可能会像下面这样:

1
2
let flag = url.startsWith("https:");
let http = flag ? require("https") : require("http");

虽然可能只需要一两行代码,但如果有多个地方需要使用Web服务,开发人员可能还要考虑封装判断逻辑,这就比较麻烦了。

Electron 为我们提供了一个 net 模块,允许我们使用 Chromium 的原生网络库发出 HTTP 或 HTTPS 请求,
它内部会自动判断请求地址是基于什么协议的,代码如下:

1
2
3
4
5
6
7
8
const { net } = require("electron");
const request = net.request("https:// www.cnblogs.com/aggsite/AggStats");
request.on("response", response => {
    let html = "";
    response.on("data", data => (html += data));
    response.on("end", () => console.log(html));
});
request.end();

net 模块是一个主进程模块,大部分时候都不应该在渲染进程中通过 remote 使用 net 模块,这跟我们前面讲的道理是一样的。
请求是在主进程发起的,但 response 回调函数是在渲染进程内注册的,当主进程发起请求后,会异步地通知渲染进程的 response 回调函数,很有可能你的 end 事件还没注册,主进程的 response 回调函数就已经执行完了。

扩展: 
Electron7.x.y 版本 net 模块尚存在一个 bug:使用 net.request 发起请求后在响应回调中 response.headers["set-cookie"] 为空,也就是说开发者拿不到后端响应的Cookie数据。
虽有解决办法(https://github.com/electron/electron/issues/20631#issuecomment-549338384),但比较麻烦,且解决办法也存在局限性。
如果需要获取响应的 Cookie,建议等官方升级后再使用此模块。

在渲染进程中如果不想自己封装 XMLHttpRequest,你可以考虑使用第三方库—— superagenthttps://github.com/visionmedia/superagent)或 axioshttps://github.com/axios/axios),这两个都是封装精良的HTTP请求库,既支持 Node.js 环境又支持浏览器环境。

除 XMLHttpRequest 和第三方库外,你还可以使用 HTML5 的新 API——Fetch,使用它你就既不需要额外的封装工作,也不用引入任何第三方库了。
以下为Fetch的简单用法:

1
2
3
let res = await fetch(your_url);
let json = await res.json();
console.log(json);

使用 WebSocket 通信

WebSocket是HTML5提供的一个Web客户端与服务端进行全双工通信的协议。

在没有WebSocket之前,Web服务端很难给客户端(浏览器)发送一条消息,很多开发者都是使用AJAX轮询来解决这个问题的。
轮询是在特定的时间间隔(如每1秒)内由浏览器对服务器发出HTTP请求,然后由服务器返回数据给客户端的浏览器,以达到服务器给浏览器推送数据的目的(只是看起来像而已)。

这种技术有明显的缺点,即浏览器需要不断地向服务器发出请求,然而HTTP请求可能包含较长的HTTP头信息,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽资源。
而且轮训的时间间隔太短会给服务器造成很大的压力,轮训的间隔太长会导致消息的时效性又无法保证。

WebSocket允许服务端主动向客户端推送数据,使客户端和服务器之间的数据交换变得更加简单。
在WebSocket中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
可见WebSocket协议能更好地节省服务器资源和带宽,并且能够实时进行通信。

使用WebSocket与Web服务器进行通信是纯前端开发技术,与Electron关系不大。
下面是一段WebSocket客户端代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
let websocket = new WebSocket('ws:// localhost:8080/sockjs-node/646/iuaimdwk/
websocket');
websocket.onopen = function(evt) {         // 连接打开时触发此事件
    console.log('open')
};
websocket.onclose = function(evt) {         // 连接关闭时触发此事件
    console.log('close')
};
websocket.onmessage = function(evt) {        // 收到消息时触发此事件
    console.log(evt.data)
};
websocket.onerror = function(evt) {        // 产生异常时触发此事件
    console.log(evt.data)
};

以上代码可以在Electron应用内正常运行,当服务端有数据推送过来时,触发WebSocket对象的onmessage事件。
如果需要向服务端发送数据可以使用如下命令:

1
websocket.send("此为需发送的字符串");

如果想主动关闭连接,可以使用如下命令:

1
websocket.close();

扩展: 
很多开发者知道如何调试Node.js的代码,但并不知道其背后的原理。
当开发者启动一个Node服务时,Node环境会为开发者启动一个相应的WebSocket监听服务,用以调试这个Node服务的代码,形如:

1
> Debugger listening on ws://127.0.0.1:9229/5478304f-be2b-40c6-ac5a-be82aedf97d7

当代码运行到一个断点时,Node会通过这个WebSocket服务给监听此服务的客户端(也就是调试工具)发送一个消息。
这个消息内包含当前被调试源码的中断的行号、列号、文件路径等信息。
当开发者在调试工具中点击继续执行时,调试工具会给这个WebSocket服务发送一个消息,消息体内包含“继续执行”命令。

同样开发者操作调试工具单步跳过、单步进入,甚至增加、删除一个断点都会以消息的形式发送给这个WebSocket服务,由此服务来控制Node.js程序的运行。
这些消息的格式是由谷歌工程师定义的,具体内容可参阅:https://github.com/buggerjs/bugger-v8-client/blob/master/PROTOCOL.md#event-message

截获并修改网络请求

当应用中嵌入了第三方网页时,我们往往希望能获得更多的操控第三方网页的权力,在前文中我们介绍了读写受限访问的Cookie、禁用同源策略以发起跨域请求等技术,接下来我们介绍如何截获并修改网络请求。

如果只是要截获AJAX请求,那么为第三方网页注入一个脚本,让这个脚本修改XMLHttpRequest对象以获取第三方网页AJAX请求后的数据即可,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let open = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function (method, url, async, user, 
pass) {
    this.addEventListener("readystatechange", function () {
        if (this.readyState === 4 && this.status === 200) {
            console.log(this.responseText);  //这是服务端响应的数据
        }
    }, false);
    open.apply(this, arguments);
}

在上面代码中,我们首先把XMLHttpRequest原型链上的open方法保存到一个变量中,接着为XMLHttpRequest原型链定义了一个新的open方法,
在这个新的open方法内部我们监听了XMLHttpRequest对象的readystatechange事件,一旦readyState值变为4并且status值为200时,我们即认为有AJAX请求成功,
同时,把得到的响应数据打印在控制台上,得到的响应数据即为你要截获的数据。
在新的open方法的最后我们调用了原始的open方法,以使浏览器正确地发起AJAX请求。

重点:  
apply方法可以调用并执行一个具体的函数,与普通的函数调用方式不同的是apply方法可以为函数指定this的值,以及作为一个数组提供的参数。
与apply方法类似的还有另外一个工具方法call,call方法也可以为被调函数指定this的值,
区别在于call方法接受的是参数列表,而不是一个参数数组。

另外,XMLHttpRequest对象的readyState属性可能的值及其含义如下所示。

  • 0:请求未初始化。
  • 1:服务器连接已建立。
  • 2:请求已接收。
  • 3:请求处理中。
  • 4:请求已完成,且响应已就绪。

status属性的值为HTTP响应的状态码,值为200时表示响应成功,3xx时表示请求重定向,4xx时表示请求错误,5xx时表示服务器错误。

在网页加载之初,注入并执行上面的代码,就为接下来的每个AJAX请求都注册了一个监听器,
开发者可以通过open方法的method、url等参数过滤具体的请求,以实现在不影响第三方网页原有逻辑的前提下,精准截获AJAX响应数据的目的。

这种方法虽然可以正确截获AJAX请求的响应数据,但对截获静态文件请求及修改响应数据无能为力。
如果开发者想获得这方面的能力,可以使用Electron提供的webRequest对象的方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
this.win.webContents.session.webRequest.onBeforeRequest({urls: ["https://*/*"]}, 
(details, cb) => {
    if (details.url === 'https://targetDomain.com/vendors.js') {
        cb({
            redirectURL: 'http://yourDomain.com/vendors.js'
        });
    } else {
        cb({ })
    }
});

在上面代码中,我们使用了 webRequestonBeforeRequest 方法监听第三方网页的请求,当请求发生时,判断请求的路径是否为我们关注的路径,如果是,则把请求转发到另一个地址。
新的地址往往是我们自己服务器的一个地址。

onBeforeRequest 方法的第一个参数为一个过滤器对象,此处我们过滤了所有HTTPS的请求。
第二个参数为监听器方法,该方法的第一个参数details拥有url、method、referrer等请求信息,
我们可以根据这些信息更精准地判断请求的内容,第二个参数是一个回调函数,监听到具体的请求后,如果需要转发请求,则给这个回调方法传递一个包含redirectURL属性的对象;
如果需要终止请求,可以给这个方法传递一个包含cancel属性的对象;
如果不需要做任何额外操作,继续执行现有请求,那么只要给这个方法传递一个空对象即可。

通过上面的方法,我们就可以完美截获所有的请求并修改请求的响应。
有的时候开发者可能希望修改第三方网页内某个js文件的代码逻辑,那么这个技术将能有效地满足此类需求。

除此之外,Electron 还提供了 onBeforeSendHeadersonHeadersReceivedonCompletedonErrorOccurred 等方法供开发者使用,
但这些方法除了能获得响应的头的信息外,都无法得到或修改具体的响应数据,相对来说 onBeforeRequest 方法的价值更大。

与系统内其他应用通信

Electron 应用与其他应用通信

系统内进程间通信一般会使用IPC命名管道技术来实现(此技术在类UNIX系统下被称为域套接字),Electron并没有提供相应的API,我们是通过Electron内置的Node.js使用此技术的。

IPC命名管道区分客户端和服务端,其中服务端主要用于监听和接收数据,客户端主要用于连接和发送数据。
服务端和客户端是可以做到持久连接双向通信的。

假设有一个第三方程序,需要发送数据给我们的程序,我们可以在Electron应用中创建一个命名管道服务以接收数据,代码如下:

1
2
3
4
5
6
7
8
9
let net = require('net');
let PIPE_PATH = "\\\\.\\ pipe\\ mypipe";
let server = net.createServer(function(conn) {
    conn.on('data', d => console.log(`接收到数据:${d.toString()}`));
    conn.on('end', () => console.log("客户端已关闭连接"));
    conn.write('当客户端建立连接后,发送此字符串数据给客户端');
});
server.on('close', () => console.log('服务关闭'));
server.listen(PIPE_PATH, () => console.log('服务启动,正在监听'));

在上面代码中,我们通过Node.js的net模块创建了一个命名管道服务对象server,然后让这个server监听一个命名管道地址。
当有客户端连接此命名管道服务时,将触发createServer的回调函数。

此回调函数有一个connection对象传入,开发者可以用此对象接收或发送数据。
当有数据传入时,会触发 connection 对象的 'data' 事件;当连接关闭时,会触发 connection 对象的 'end' 事件;
开发者也可以通过 connection 对象的 write 方法向客户端发送数据。

当你的应用程序不再需要接收其他应用发来的数据时,需要关闭命名管道服务 server.close(),服务关闭后server对象会收到'close'事件。

用反引号可以包裹普通字符串,也可以包裹多行字符串,或者在字符串中嵌入变量。比如:

1
2
3
4
5
6
7
8
// 多行字符串
let str1 = `大段的文本往往需要换行,
以前在程序中把多行文本赋值给一个变量很麻烦,
有了字符串模板,这个工作就很简单了。`;
console.log(str1);
// 在模板字符串中嵌套变量
let str2 = `你可以通过${str1}这种方式把另一个变量引入到此字符串中`;
console.log(str2);

运行上面的代码,你会发现 str1 里是包含换行符的,不用再特意加入 \n 来控制字符串内的换行。

在上面的例子中,我们使用

1
console.log(`接收到数据:${d.toString()}`)

时,就是在模板字符串中嵌入了变量。

假设一个第三方程序已经开启了命名管道服务,需要我们的程序给它发送数据,那么可以在我们的应用中创建一个命名管道客户端,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let net = require("net");
let PIPE_PATH = '\\\\.\\ pipe\\mypipe';
let client = net.connect(PIPE_PATH, () => {
    console.log("连接建立成功");
    client.write("这是我发送的一个第一个数据包");
});
client.on("data", d => {
    console.log('接收到的数据:${d.toString()}');
    client.end("这是我发送的第二个数据包,发送完之后我将关闭");
});
client.on("end", () => console.log("服务端关闭了连接"));

此处,我们通过net模块的connect方法连接了一个命名管道的服务端,该方法返回一个client对象,我们可以通过该对象监听数据传入和连接关闭事件。

在连接建立成功的回调函数里,我们通过client.write方法向服务端发送一条消息。
在接收到服务端的消息后,我们又通过client.end向服务端发送第二条消息。
如果你给client.end方法传递了数据,则相当于调用client.write(data)之后再调用client.end()。

调用client.end方法后,连接关闭,服务端也会触发相应的'close'事件。

如果用我们上文编写的客户端连接服务端,将得到如下输出:

1
2
3
4
5
6
7
8
9
> 服务端程序控制台输出:
服务启动,正在监听
接收到数据:这是我发送的一个第一个数据包
接收到数据:这是我发送的第二个数据包,发送完之后我将关闭
客户端已关闭连接
> 客户端程序控制台输出:
连接建立成功
接收到的数据:当客户端建立连接后,发送此字符串数据给客户端
服务端关闭了连接

除了命名管道外,你还可以通过socket、剪切板、共享文件(通过监控文件的变化来实现应用间通信)等方法来与其他进程通信,但最常见的还是命名管道。

网页与 Electron 应用通信

由于浏览器中的网页运行在沙箱环境下,网页没有建立或访问命名管道的能力,因此也无法主动与系统内其他应用程序通信。
如果我们想使用传统的技术解决这个问题,就需要开发原生浏览器插件,让原生浏览器插件与系统内应用程序通信,然而浏览器对第三方插件限制较多,用户授权安装第三方插件困难重重。

这时就需要另外一种思路来解决这个问题了。

如果你同时是网页和应用程序的开发者,可以先在Electron应用程序内启动一个HTTP服务,然后再在网页内跨域访问这个HTTP服务,即可完成网页与Electron应用的通信。
Electron应用内启动HTTP服务的代码如下:

 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
var http = require('http');
let server = http.createServer((request, response) => {
    if (request.url == "/helloElectron1") {
        let jsonString = '';
        request.on('data', data => jsonString += data);
        request.on('end', () => {
            let post = JSON.parse(jsonString);
            // 此处处理你的业务逻辑
            response.writeHead(200, {
                "Content-Type": "text/html",
                "Access-Control-Allow-Origin": "*"
            });
            let result = JSON.stringify({
                "ok": true,
                "msg": "请求成功"
            });
            response.end(result);
        });
        return;
    }
});
server.on("error", err => {
    // 此处可把错误消息发送给渲染进程,由渲染进程显示错误消息给用户。
});
server.listen(9416);

程序通过 http.createServer 创建了一个Web服务,该服务监听本机的 9416 端口。
注意,createServer 方法返回的server实例应该是一个全局对象或者挂载在全局对象下,目的是避免被垃圾收集器回收。

当网页请求 http://localhost:9416/helloElectron1 时,Electron 应用会接到此请求,并获取到网页发送的数据,同时通过 response 的 writeHead 和 end 方法给网页响应数据。

代码 server.listen(9416) 控制服务监听9416端口,此处为了方便演示,就只监听了一个固定的端口。
但这个端口很有可能在用户系统中已经被占用了,这种情况下你的服务将启动失败,所以商用产品一定要先确认端口可用,再进行监听。

你可以给 server.listen 方法传入0或什么也不传,让 Node.js 自动给你选一个可用的端口进行监听。
一旦 Node.js 找到可用端口,开始监听后,会触发 'listening' 事件。在此事件中你可以获取 Node.js 到底给你监听的是哪个端口,代码如下:

1
2
3
4
server.on('listening', () => {
    console.log(server.address().port);
})
server.listen(0);

你可以把这个端口上报给网页的服务器,然后再由服务器下发给网页前端JavaScript,此时网页与Electron应用通信,就知道要请求什么地址了。

自定义协议(protocol)

有的时候我们不希望应用使用HTTP协议(http://)加载界面内容,因为这要在本地创建一个HTTP服务,产生额外的消耗。
我们也不希望通过File协议(file://)加载界面内容,因为它不能很好地支持路径查找,比如,你没办法通过 “/logo.png” 这样的路径查找根目录下的图片,因为这种协议不知道你的根目录在哪儿。

此时就需要用到Electron的自定义用户协议了。
它允许开发者自己定义一个类似HTTP或File的协议,当应用内出现此类协议的请求时,开发者可以定义拦截函数,处理并响应请求。

在正式自定义用户协议之前,我们一般会先告诉Electron框架我打算声明一个怎样的协议。
这个协议具备一定的特权,这里的特权是指该协议下的内容不受CSP(Content-Security-Policy内容安全策略)限制,可以使用内置的Fetch API等。

1
2
3
let { protocol } = require('electron');
let option = [{ scheme: 'app', privileges: { secure: true, standard: true } }];
protocol.registerSchemesAsPrivileged(option);

此代码务必在程序启动之初,app的ready事件触发之前执行,且只能执行一次。
代码中,通过scheme属性指定了协议名称为app,与 “http://” 类似,接下来我们使用 “app://” 请求我们应用内的资源。
下面我们来真正地注册这个自定义协议:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
let { protocol } = require('electron');
let path = require('path');
let { readFile } = require('fs');
let { URL } = require('url');
protocol.registerBufferProtocol('app', (request, respond) => {
        let pathName = new URL(request.url).pathname;
        pathName = decodeURI(pathName);
        let fullName = path.join(__dirname, pathName);
        readFile(fullName, (error, data) => {
            if (error) { console.log(error); }
            let extension = path.extname(pathName).toLowerCase()
            let mimeType = ''
            if (extension === '.js') mimeType = 'text/javascript' 
            else if (extension === '.html')  mimeType = 'text/html' 
            else if (extension === '.css')   mimeType = 'text/css'
            else if (extension === '.json')  mimeType  = 'application/json'
            respond({ mimeType, data })
        })
    },
    error => { if (error) { console.log(error); } }
)

如你所见,这和启动一个HTTP服务没有太大的区别。
一开始我们通过 protocol 的 registerBufferProtocol 方法,注册了一个基于缓冲区的协议,registerBufferProtocol 方法接收一个回调函数,当用户发起基于 “app://” 协议的请求时,此回调函数会截获用户的请求。

在回调函数内,先获取到用户请求的文件的绝对路径,然后读取文件内容,接着按指定的mimeType响应请求。
这其实就是一个简单的静态文件服务。

自定义协议注册完成之后,就可以通过如下方式使用此自定义协议了:

1
win.loadURL('app:// ./index.html');

如果你是使用Vue CLI Plugin Electron Builder创建的项目,你会发现编译后的index.html在引用静态资源时都变成了我们指定的协议:

1
2
3
4
5
<link href=app:// ./js/about.2bd39519.js rel=prefetch>
<link href=app:// ./css/app.1624a0db.css rel=preload as=style>
<link href=app:// ./js/app.6b55e57d.js rel=modulepreload as=script>
<link href=app:// ./js/chunk-vendors.d7eee39c.js rel=modulepreload as=script>
<link href=app:// ./css/app.1624a0db.css rel=stylesheet>

在使用自定义协议访问某页面时,你还是可以在此页面中使用HTTP协议引用外部资源的,它们之间是兼容的:

1
2
<link rel="stylesheet" type="text/css" href="https:// at.alicdn.com/t/font.css">
<img src="https:// www.cnblogs.com/images/logo_small.gif" alt="">

以上是我们基于缓冲区注册的自定义用户协议,除此之外你还可以基于File协议注册用户自定义协议registerFileProtocol,基于HTTP协议注册用户自定义协议 registerHttpProtocol,基于字符串注册用户自定义协议 registerStringProtocol

无论以何种方式注册自定义协议,其目的都是更好地提升应用自身的开发便利性,所得到的协议都只适用于当前应用程序内部,系统中的其他应用无法使用该协议。

使用 socks5 代理

现实环境中,无论是内网还是外网都会面对种种限制,为了应对这些特殊的情况,我们就需要使用代理。

当你的电脑A无权访问Internet,而另一台电脑B可以访问时,此时就可以让电脑A先连接电脑B,然后通过电脑B来访问Internet。
那么电脑B就是电脑A的代理服务器。

常见的代理服务器有HTTP代理、HTTPS代理和socks代理,socks代理隐蔽性更强,效率更高,速度更快。
接下来将讲解如何使用Electron内置的socks代理访问网络服务。

1
2
3
4
let result = await win.webContents.session.setProxy({ 
    proxyRules: 'socks5://58.218.200.249:2071' 
});
win.loadURL('https://www.ipip.net');

上面代码中,我们使用session实例的setProxy方法来为当前页面设置代理。
socks代理协议有两个常见的版本——socks4和socks5,此处我们使用了适应性更强的socks5代理。

代理设置成功后,我们马上使网页加载了一个IP地址查询的网址,在此页面上我们可以看到访问该页面的实际IP地址,如图9-1所示。

图9-1scoks5代理设置成功后网页显示数据

图9-1 scoks5代理设置成功后网页显示数据

如果这里显示的地址是你的代理服务器所在地的地址(IP地址可能会有差异),那么说明代理设置成功。

以上这种方法可以给单个渲染进程设置代理,如果你需要给整个应用程序设置代理,可以使用如下代码完成:

1
app.commandLine.appendSwitch('proxy-server', 'socks5:// 58.218.200.249:2071');