笔者看了很多文章,都有提到需要自建网络监控,但没有开源具体的实现方式,笔者在此摸索了一下,仅供大家参考。
背景
Electron 官方虽然有提供网络在线/离线探测方案,但这个方案是基于浏览器本身的。使用浏览器标准 APInavigator.onLine
来判断网络环境 Electron online-offline-events。
这个方式虽然是 W3C 标准的,但在多篇文章中,都提到这个方式在线/离线情况是不准确的,它是基于本地网络判断,就是在局域网下也是会返回有网,不能说明网络环境是在线的情况。
如何实时获取可靠的网络环境,对于桌面端用户体验来说也是至关重要的。
分析
笔者确实没有找到现成的方案可以采用,从语雀桌面端技术架构实践中也只是提到他们使用了系统级的网络监测方案,但没有说是基于什么技术来做的。笔者猜测可能会底层一些,比如链接 C 库来实现。但笔者公司桌面端项目还没有成熟,贸然引入多技术栈,会带来额外的负担和成本。
那从需要的结果倒推,我们需要:
- 具有网络实时检测能力。
- 可以判断是能连到广域网络环境的。
- 最好是系统级的方法。
那在这个基础上,笔者想了几种方案:
- 可以用
electron-edge-js
来调用 .Net 本地服务,这个包现在也是在项目中使用的,所以不会增大应用体积。但它只对 Windows 生效,Mac 需要安装 .Net Core,对于我们项目来说不合适。 - Electron 提供了net.resolveHost 方法,可以解析当前域名,但我们当前 Electron 版本没有这个 API 可用。
- 项目中可以使用
shelljs
,那我们定时启动终端去ping
域名是否可行?可以是可以,但也可以不引用shelljs
来实现。
方案
- 使用单独子进程监听(不增加主进程)。
- 使用 node 的 DNS 模块 dns 我们主站域名,如果拿得到 IP,可认为网络环境正常。
- 每秒执行一次上次操作,缓存至内存,提供广播事件给监听者。
- 主进程广播事件,渲染进程监听事件。
实现
子进程执行
const dns = require('dns');
const process = require('process');
const options = {
family: 4,
servers: ['8.8.8.8', '114.114.114.114'],
};
const host = 'www.gaoding.com';
let networkStatus = true;
setInterval(() => {
dns.lookup(host, options, (err, _) => {
if (err) {
console.error(`[network-monitor] error: ${err.message}`);
}
emitNetworkStatusDidChange(err ? false : true);
});
}, 1000);
function emitNetworkStatusDidChange(status) {
if (networkStatus === status) {
return;
}
networkStatus = status;
process.send({
type: 'network_status',
content: status,
});
}
- 通过 DNS 轮询主站域名,来判断是否有网络。
- 通过
process
发送消息给主进程处理。 - 通过
networkStatus
变更来监听网络变化,减少通信成本。
主进程监控
import Module from 'module';
import { ChildProcess, fork } from 'child_process';
/**
* 网络环境监听管理
*/
export class GDNetWatchManager {
...
private isOnline: boolean = true;
private child?: ChildProcess;
/**
* 启动监听管理
*/
public start() {
const path = Module._resolveFilename('@gaoding/network-monitor', module, true);
const child = fork(path);
child.on('message', (data: any) => {
if (data.type === 'network_status') {
!data.content && console.error(`网络连接异常`);
this.changeStatus(data.content);
}
});
this.child = child;
}
...
// ================== Private Methods ================ //
private changeStatus(online: boolean) {
if (this.isOnline === online) {
return;
}
this.isOnline = online;
GNBManager.shared.network.emitNetworkStatusDidChangeEvent(
online,
online ? 'unknown' : 'none',
);
}
}
child_process.fork
来构建具有 node 环境的子进程,Electron 版本支持的话,也可以选择 utility-process- 通过
child_process.on
来监听消息变化。 GNBManager.shared.network.emitNetworkStatusDidChangeEvent
是发送事件广播,这里用了跨端工具链的实现(具体桌面端如何实现待作者后续文章补充)
任意页面监听事件
GNB.event.network.onNetworkStatusDidChangeEvent((online, type) => {
... //监听并处理
})
后续
一个网络监听就开一个子进程是很浪费,所以后续会归并到守护进程中来增强整体性能。
本文只是桌面端开发过程中遇到的很小的一点,希望能抛砖引玉。
感谢阅读,如果对你有用请点个赞 ❤️
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END