首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >鸿蒙 PC 使用 Electron 实现 IP 地址获取功能详解

鸿蒙 PC 使用 Electron 实现 IP 地址获取功能详解

作者头像
徐建国
发布2025-11-29 15:19:33
发布2025-11-29 15:19:33
3000
举报
文章被收录于专栏:个人路线个人路线

鸿蒙 PC 使用 Electron 实现 IP 地址获取功能详解

问题背景

在桌面应用开发中,获取设备的网络信息是一个常见的需求。无论是网络诊断工具、系统监控应用,还是需要显示设备连接状态的应用程序,都需要能够获取和显示设备的 IP 地址信息。

需求分析

  1. 获取本地 IP 地址:获取设备所有网络接口的 IPv4 地址
  2. 获取主机名:显示设备的网络主机名
  3. 获取公网 IP:可选功能,获取设备在互联网上的公网 IP 地址
  4. 跨平台兼容:在 Windows、macOS、Linux 以及鸿蒙 PC 平台上都能正常工作
  5. 信息完整性:显示 IP 地址、子网掩码、MAC 地址等完整网络信息
  6. 用户体验优化:清晰展示所有网络接口,按优先级排序

技术挑战

  1. 平台差异:不同操作系统的网络接口命名和结构不同
  2. 接口过滤:需要正确过滤内部接口(loopback)和 IPv6 地址
  3. 公网 IP 获取:需要通过网络请求获取,可能失败或超时
  4. 接口排序:需要智能排序,优先显示重要的网络接口(以太网、WiFi)
  5. 鸿蒙平台适配:需要确保在鸿蒙 PC 平台上也能正常工作

实现方案

方案对比

方案

优点

缺点

适用场景

Node.js os.networkInterfaces()

跨平台,无需额外依赖,简单可靠

无法直接获取公网 IP

推荐使用,最可靠

系统命令(ifconfig/ipconfig)

可获取详细信息

需要解析输出,平台差异大

复杂场景

第三方网络库

功能完整

增加依赖,可能过度设计

特殊需求

最终方案

采用Node.js 内置模块 + 网络请求的组合方案:

  1. **使用 os.networkInterfaces()**:获取所有网络接口的本地 IP 地址 - Node.js 内置 API,跨平台兼容
  2. **使用 os.hostname()**:获取设备主机名
  3. HTTP 请求获取公网 IP:通过多个公共服务尝试获取公网 IP 地址
  4. 智能接口排序:按接口类型(以太网、WiFi)和名称优先级排序
  5. 错误处理机制:优雅处理网络请求失败和超时情况

方案优势

  • 跨平台兼容:支持 Windows、macOS、Linux 和鸿蒙 PC 平台
  • 无需额外依赖:使用 Node.js 内置模块,轻量级
  • 信息完整:获取 IP 地址、子网掩码、MAC 地址等完整信息
  • 用户体验好:智能排序,清晰展示网络接口信息
  • 容错性强:公网 IP 获取失败不影响本地 IP 显示

代码实现

1. 主进程实现(main.js)

在主进程中实现 IP 地址获取功能:

代码语言:javascript
复制
const { app, BrowserWindow, ipcMain } = require('electron');
const os = require('os');
const https = require('https');
const http = require('http');

// 获取IP地址
asyncfunction getIPAddresses() {
const platform = process.platform;
console.log('获取IP地址,平台:', platform);

try {
    // 使用Node.js内置模块获取网络接口信息
    const networkInterfaces = os.networkInterfaces();
    const ipAddresses = [];

    // 遍历所有网络接口
    for (const [interfaceName, addresses] ofObject.entries(networkInterfaces)) {
      if (!addresses) continue;

      for (const address of addresses) {
        // 跳过内部(loopback)和非IPv4地址
        if (address.internal || address.family !== 'IPv4') {
          continue;
        }

        ipAddresses.push({
          interface: interfaceName,
          address: address.address,
          netmask: address.netmask,
          mac: address.mac || null
        });
      }
    }

    // 按接口名称排序(优先显示以太网和WiFi接口)
    ipAddresses.sort((a, b) => {
      const priority = (name) => {
        const lower = name.toLowerCase();
        if (lower.includes('ethernet') || lower.includes('eth')) return1;
        if (lower.includes('wifi') || lower.includes('wlan') || lower.includes('airport')) return2;
        if (lower.includes('en0') || lower.includes('en1')) return3;
        return4;
      };
      return priority(a.interface) - priority(b.interface);
    });

    // 获取主机名
    const hostname = os.hostname();

    // 尝试获取公网IP(可选,需要网络请求)
    let publicIP = null;
    try {
      // 使用多个服务尝试获取公网IP
      const publicIPServices = [
        'https://api.ipify.org?format=json',
        'https://api.ip.sb/ip',
        'https://ifconfig.me/ip'
      ];

      for (const service of publicIPServices) {
        try {
          const parsedUrl = new URL(service);
          const client = parsedUrl.protocol === 'https:' ? https : http;

          const result = awaitnewPromise((resolve, reject) => {
            const req = client.get(service, (res) => {
              let data = '';
              res.on('data', (chunk) => { data += chunk; });
              res.on('end', () => {
                try {
                  const json = JSON.parse(data);
                  resolve(json.ip || data.trim());
                } catch (e) {
                  resolve(data.trim());
                }
              });
            });
            req.on('error', reject);
            req.setTimeout(3000, () => {
              req.destroy();
              reject(newError('Timeout'));
            });
          });

          // 验证IP地址格式
          if (result && /^\d+\.\d+\.\d+\.\d+$/.test(result)) {
            publicIP = result;
            console.log('获取到公网IP:', publicIP);
            break;
          }
        } catch (error) {
          console.warn(`从 ${service}获取公网IP失败:`, error.message);
          continue;
        }
      }
    } catch (error) {
      console.warn('获取公网IP失败:', error.message);
    }

    return {
      hostname: hostname,
      interfaces: ipAddresses,
      publicIP: publicIP,
      platform: platform
    };
  } catch (error) {
    console.error('获取IP地址失败:', error);
    return {
      hostname: os.hostname(),
      interfaces: [],
      publicIP: null,
      platform: platform,
      error: error.message
    };
  }
}

// IPC处理器:获取IP地址
ipcMain.handle('get-ip-addresses', async (event) => {
console.log('收到获取IP地址请求');
try {
    const ipInfo = await getIPAddresses();
    console.log('IP地址信息:', ipInfo);
    return ipInfo;
  } catch (error) {
    console.error('获取IP地址错误:', error);
    return {
      hostname: os.hostname(),
      interfaces: [],
      publicIP: null,
      platform: process.platform,
      error: error.message
    };
  }
});

2. 预加载脚本实现(preload.js)

在预加载脚本中暴露安全的 API 给渲染进程:

代码语言:javascript
复制
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  // 获取IP地址
  getIPAddresses: () => {
    return ipcRenderer.invoke('get-ip-addresses');
  }
});

3. 渲染进程实现(index.html)

在渲染进程中调用 API 并显示 IP 地址信息:

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>IP地址获取示例</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
            padding: 20px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
        }

        button {
            padding: 15px30px;
            font-size: 16px;
            font-weight: 600;
            color: white;
            background: rgba(255, 255, 255, 0.2);
            border: 2px solid white;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s;
            backdrop-filter: blur(10px);
        }

        button:hover {
            background: rgba(255, 255, 255, 0.3);
            transform: translateY(-2px);
        }

        #ip-info {
            margin-top: 20px;
            padding: 20px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 10px;
            color: white;
            backdrop-filter: blur(10px);
            max-width: 800px;
        }
    </style>
</head>
<body>
    <button onclick="getIPAddresses()">🌐 获取IP地址</button>

    <div id="ip-info" style="display: none;">
        <h2>IP地址信息</h2>
        <div id="ip-details"></div>
    </div>

    <script>
        // 获取IP地址
        async function getIPAddresses() {
            console.log('点击获取IP地址按钮');
            const ipInfoDiv = document.getElementById('ip-info');
            const ipDetailsDiv = document.getElementById('ip-details');

            try {
                if (window.electronAPI && window.electronAPI.getIPAddresses) {
                    console.log('使用 Electron IPC 获取IP地址');
                    const ipInfo = await window.electronAPI.getIPAddresses();
                    displayIPInfo(ipInfo);
                } else {
                    alert('IP地址功能不可用');
                }
            } catch (error) {
                console.error('获取IP地址失败:', error);
                ipDetailsDiv.innerHTML = '<p style="color: #ff6b6b;">获取IP地址失败: ' + error.message + '</p>';
                ipInfoDiv.style.display = 'block';
            }
        }

        function displayIPInfo(ipInfo) {
            const ipInfoDiv = document.getElementById('ip-info');
            const ipDetailsDiv = document.getElementById('ip-details');

            let html = '';

            // 显示错误信息
            if (ipInfo.error) {
                html += `<div style="color: #ff6b6b; margin-bottom: 10px;">
                    <strong>错误:</strong> ${ipInfo.error}
                </div>`;
            }

            // 显示平台信息
            if (ipInfo.platform) {
                html += `<div style="margin-bottom: 15px;">
                    <strong>平台:</strong><span style="color: #51cf66;">${ipInfo.platform}</span>
                </div>`;
            }

            // 显示主机名
            if (ipInfo.hostname) {
                html += `<div style="margin-bottom: 15px;">
                    <strong>主机名:</strong>
                    <span style="color: #51cf66; font-size: 18px; font-weight: bold;">${ipInfo.hostname}</span>
                </div>`;
            }

            // 显示公网IP
            if (ipInfo.publicIP) {
                html += `<div style="margin-bottom: 15px; padding: 10px; background: rgba(81, 207, 102, 0.2); border-radius: 5px; border-left: 3px solid #51cf66;">
                    <strong>公网IP:</strong>
                    <span style="color: #51cf66; font-size: 18px; font-weight: bold; font-family: monospace;">${ipInfo.publicIP}</span>
                </div>`;
            } else {
                html += `<div style="margin-bottom: 15px; padding: 10px; background: rgba(255, 212, 59, 0.2); border-radius: 5px;">
                    <strong>公网IP:</strong>
                    <span style="color: #ffd43b;">无法获取(可能需要网络连接)</span>
                </div>`;
            }

            // 显示网络接口
            if (ipInfo.interfaces && ipInfo.interfaces.length > 0) {
                html += `<div style="margin-top: 20px;">
                    <strong style="font-size: 16px;">网络接口 (${ipInfo.interfaces.length}个):</strong>
                </div>`;

                html += '<div style="margin-top: 10px;">';
                ipInfo.interfaces.forEach((iface, index) => {
                    html += `<div style="margin-bottom: 15px; padding: 15px; background: rgba(255, 255, 255, 0.05); border-radius: 8px; border-left: 3px solid #51cf66;">
                        <div style="margin-bottom: 8px;">
                            <strong>接口 ${index + 1}:</strong>
                            <span style="color: #51cf66; font-weight: bold;">${iface.interface}</span>
                        </div>
                        <div style="margin-bottom: 5px;">
                            <strong>IP地址:</strong>
                            <span style="color: #51cf66; font-size: 16px; font-weight: bold; font-family: monospace;">${iface.address}</span>
                        </div>
                        <div style="margin-bottom: 5px;">
                            <strong>子网掩码:</strong>
                            <span style="color: #ffd43b; font-family: monospace;">${iface.netmask}</span>
                        </div>`;

                    if (iface.mac) {
                        html += `<div>
                            <strong>MAC地址:</strong>
                            <span style="color: #ffd43b; font-family: monospace;">${iface.mac}</span>
                        </div>`;
                    }

                    html += `</div>`;
                });
                html += '</div>';
            } else {
                html += `<div style="margin-top: 15px; padding: 10px; background: rgba(255, 107, 107, 0.2); border-radius: 5px;">
                    <strong>网络接口:</strong>
                    <span style="color: #ff6b6b;">未找到可用的网络接口</span>
                </div>`;
            }

            ipDetailsDiv.innerHTML = html;
            ipInfoDiv.style.display = 'block';
        }
    </script>
</body>
</html>

核心实现要点

1. 网络接口过滤

代码语言:javascript
复制
// 跳过内部(loopback)和非IPv4地址
if (address.internal || address.family !== 'IPv4') {
  continue;
}
  • **address.internal**:过滤掉回环接口(127.0.0.1)
  • **address.family !== 'IPv4'**:只保留 IPv4 地址,过滤 IPv6

2. 接口智能排序

代码语言:javascript
复制
ipAddresses.sort((a, b) => {
const priority = (name) => {
    const lower = name.toLowerCase();
    if (lower.includes('ethernet') || lower.includes('eth')) return1;
    if (lower.includes('wifi') || lower.includes('wlan') || lower.includes('airport')) return2;
    if (lower.includes('en0') || lower.includes('en1')) return3;
    return4;
  };
return priority(a.interface) - priority(b.interface);
});

优先级排序规则:

  1. 以太网接口etherneteth - 优先级最高
  2. WiFi 接口wifiwlanairport - 优先级次之
  3. macOS 接口en0en1 - 优先级第三
  4. 其他接口:其他所有接口 - 优先级最低

3. 公网 IP 获取策略

代码语言:javascript
复制
const publicIPServices = [
  'https://api.ipify.org?format=json',
  'https://api.ip.sb/ip',
  'https://ifconfig.me/ip'
];

使用多个公共服务,按顺序尝试:

  • 容错性:如果第一个服务失败,自动尝试下一个
  • 超时控制:每个请求设置 3 秒超时,避免长时间等待
  • 格式验证:使用正则表达式验证 IP 地址格式

4. 错误处理

代码语言:javascript
复制
try {
  // 获取IP地址逻辑
} catch (error) {
  console.error('获取IP地址失败:', error);
  return {
    hostname: os.hostname(),
    interfaces: [],
    publicIP: null,
    platform: platform,
    error: error.message
  };
}
  • 本地 IP 获取失败:返回空数组,但保留主机名
  • 公网 IP 获取失败:不影响本地 IP 显示,返回 null
  • 错误信息记录:在返回对象中包含错误信息,便于调试

使用示例

基本使用

代码语言:javascript
复制
// 在渲染进程中调用
const ipInfo = awaitwindow.electronAPI.getIPAddresses();

console.log('主机名:', ipInfo.hostname);
console.log('平台:', ipInfo.platform);
console.log('公网IP:', ipInfo.publicIP);
console.log('网络接口数量:', ipInfo.interfaces.length);

// 遍历所有网络接口
ipInfo.interfaces.forEach((iface, index) => {
console.log(`接口 ${index + 1}:`, iface.interface);
console.log('  IP地址:', iface.address);
console.log('  子网掩码:', iface.netmask);
console.log('  MAC地址:', iface.mac);
});

返回数据格式

代码语言:javascript
复制
{
  hostname: "MyComputer",
platform: "darwin",
publicIP: "123.45.67.89",  // 可能为null
interfaces: [
    {
      interface: "en0",
      address: "192.168.1.100",
      netmask: "255.255.255.0",
      mac: "aa:bb:cc:dd:ee:ff"
    },
    {
      interface: "en1",
      address: "10.0.0.5",
      netmask: "255.255.0.0",
      mac: "11:22:33:44:55:66"
    }
  ],
error: null// 如果有错误,这里会有错误信息
}

平台兼容性

Windows

  • ✅ 支持所有网络接口(以太网、WiFi、虚拟网卡等)
  • ✅ 正确识别接口名称(如 以太网WLAN
  • ✅ 获取 MAC 地址和子网掩码

macOS

  • ✅ 支持所有网络接口(en0、en1 等)
  • ✅ 正确识别 WiFi 接口(airport)
  • ✅ 获取 MAC 地址和子网掩码

Linux

  • ✅ 支持所有网络接口(eth0、wlan0 等)
  • ✅ 正确识别接口类型
  • ✅ 获取 MAC 地址和子网掩码

鸿蒙 PC

  • ✅ 完全兼容 Linux 接口命名规范
  • ✅ 支持所有网络接口类型
  • ✅ 获取完整的网络信息

注意事项和最佳实践

1. 性能考虑

  • 公网 IP 获取:网络请求可能较慢,建议异步处理,不要阻塞 UI
  • 超时设置:为公网 IP 请求设置合理的超时时间(建议 3-5 秒)
  • 缓存机制:如果需要频繁获取,可以考虑缓存结果

2. 隐私和安全

  • 公网 IP 暴露:获取公网 IP 会向第三方服务发送请求,注意隐私保护
  • 网络请求:确保应用有网络访问权限
  • 错误处理:网络请求失败不应影响应用正常使用

3. 用户体验

  • 加载提示:获取公网 IP 时显示加载状态
  • 错误提示:公网 IP 获取失败时给出友好提示
  • 信息展示:按重要性排序显示网络接口

4. 代码优化

代码语言:javascript
复制
// 建议:添加缓存机制
let cachedIPInfo = null;
let cacheTime = null;
const CACHE_DURATION = 60000; // 缓存1分钟

asyncfunction getIPAddresses() {
// 如果缓存有效,直接返回
if (cachedIPInfo && cacheTime && Date.now() - cacheTime < CACHE_DURATION) {
    return cachedIPInfo;
  }

// 获取新的IP信息
const ipInfo = await fetchIPAddresses();
  cachedIPInfo = ipInfo;
  cacheTime = Date.now();

return ipInfo;
}

常见问题

Q1: 为什么获取不到公网 IP?

A: 可能的原因:

  1. 设备未连接到互联网
  2. 防火墙阻止了网络请求
  3. 所有公共服务都不可用
  4. 网络请求超时

解决方案

  • 检查网络连接
  • 增加超时时间
  • 添加更多公共服务作为备选
  • 公网 IP 获取失败不影响本地 IP 显示

Q2: 为什么有些网络接口没有显示?

A: 可能的原因:

  1. 接口被过滤(内部接口或 IPv6)
  2. 接口未激活
  3. 接口没有分配 IP 地址

解决方案

  • 检查过滤条件:address.internaladdress.family !== 'IPv4'
  • 如果需要显示 IPv6,修改过滤条件
  • 如果需要显示回环接口,移除 address.internal 检查

Q3: 在鸿蒙 PC 上是否完全兼容?

A: 是的,完全兼容。因为:

  • 使用 Node.js 内置的 os 模块,跨平台兼容
  • 鸿蒙 PC 基于 Linux 内核,接口命名规范兼容
  • 所有功能在鸿蒙 PC 上测试通过

总结

本文详细介绍了在鸿蒙 PC 平台上使用 Electron 实现 IP 地址获取功能的完整方案。通过使用 Node.js 内置的 os 模块,我们可以轻松获取设备的网络接口信息,包括本地 IP 地址、子网掩码、MAC 地址等。同时,通过 HTTP 请求可以获取设备的公网 IP 地址。

核心优势

  1. 跨平台兼容:使用 Node.js 内置模块,支持所有主流平台
  2. 信息完整:获取 IP 地址、子网掩码、MAC 地址等完整信息
  3. 智能排序:按接口类型智能排序,优先显示重要接口
  4. 容错性强:公网 IP 获取失败不影响本地 IP 显示
  5. 易于使用:简单的 API 调用,清晰的返回数据结构

适用场景

  • 网络诊断工具
  • 系统监控应用
  • 设备信息展示
  • 网络配置工具
  • 开发调试工具

通过本文的实现方案,开发者可以轻松在 Electron 应用中集成 IP 地址获取功能,为用户提供清晰的网络信息展示。

参考资料

  • Node.js os 模块文档[1]
  • Electron IPC 通信文档[2]
  • Electron Context Bridge 文档[3]

参考资料

[1]

Node.js os 模块文档: https://nodejs.org/api/os.html

[2]

Electron IPC 通信文档: https://www.electronjs.org/docs/latest/api/ipc-main

[3]

Electron Context Bridge 文档: https://www.electronjs.org/docs/latest/api/context-bridge

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-11-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大前端之旅 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 鸿蒙 PC 使用 Electron 实现 IP 地址获取功能详解
    • 问题背景
      • 需求分析
      • 技术挑战
    • 实现方案
      • 方案对比
      • 最终方案
      • 方案优势
    • 代码实现
      • 1. 主进程实现(main.js)
      • 2. 预加载脚本实现(preload.js)
      • 3. 渲染进程实现(index.html)
    • 核心实现要点
      • 1. 网络接口过滤
      • 2. 接口智能排序
      • 3. 公网 IP 获取策略
      • 4. 错误处理
    • 使用示例
      • 基本使用
      • 返回数据格式
    • 平台兼容性
      • Windows
      • macOS
      • Linux
      • 鸿蒙 PC
    • 注意事项和最佳实践
      • 1. 性能考虑
      • 2. 隐私和安全
      • 3. 用户体验
      • 4. 代码优化
    • 常见问题
      • Q1: 为什么获取不到公网 IP?
      • Q2: 为什么有些网络接口没有显示?
      • Q3: 在鸿蒙 PC 上是否完全兼容?
    • 总结
      • 核心优势
      • 适用场景
    • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档