「桌面端」Electron 实时网络环境监控

笔者看了很多文章,都有提到需要自建网络监控,但没有开源具体的实现方式,笔者在此摸索了一下,仅供大家参考。

背景

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,可认为网络环境正常。
  • 每秒执行一次上次操作,缓存至内存,提供广播事件给监听者。
  • 主进程广播事件,渲染进程监听事件。

实现

子进程执行

javascript 复制代码
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变更来监听网络变化,减少通信成本。

主进程监控

typescript 复制代码
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是发送事件广播,这里用了跨端工具链的实现(具体桌面端如何实现待作者后续文章补充)

任意页面监听事件

typescript 复制代码
GNB.event.network.onNetworkStatusDidChangeEvent((online, type) => {
   ... //监听并处理
})

后续

一个网络监听就开一个子进程是很浪费,所以后续会归并到守护进程中来增强整体性能。

本文只是桌面端开发过程中遇到的很小的一点,希望能抛砖引玉。


感谢阅读,如果对你有用请点个赞 ❤️

相关推荐
MageGojo13 小时前
做节日活动页时,如何用 API 快速生成对联内容
javascript·python·节日·对联生成
向上的车轮13 小时前
Next.js 入门指南:从零到一构建全栈应用
开发语言·javascript·ecmascript
freeinlife'13 小时前
精准秒表计时器实现---基于js
开发语言·前端·javascript
优雅格子衫13 小时前
uniapp 拍照相册选取后超级好用的裁剪组件,增加水印完全自定义
开发语言·前端·javascript·uni-app·vue
AI砖家14 小时前
前端 JavaScript 异步处理全方案详解:从回调到 Observable
开发语言·前端·javascript
柒和远方14 小时前
每日一学V010: 从 Python 回到前端:一个 AI Native 开发者的 JavaScript 底层基础补全
javascript
之歆14 小时前
Day21_电商详情页核心技术实战:从LESS预处理到复杂交互实现
开发语言·前端·javascript·css·交互·less
海鸥两三14 小时前
基于 Vue 3 + 高德地图的网格规划系统实战(有源码)
前端·javascript·vue.js
逸A14 小时前
某里v2反混淆 codec 化路上踩到的两个隐蔽坑:被清零的 salt 与 opaque loop bound
javascript·人工智能·目标跟踪
丷丩14 小时前
MapLibre GL JS第11课:获取鼠标指针坐标
前端·javascript·gis·地图·mapbox·maplibre gl js