is_online
检查互联网连接是否正常
适用于 Node.js 和浏览器 (带有捆绑器) 。
在浏览器中,已经有了navigator.onLine
,但它没用,因为它只告诉您是否有本地连接,而不是告诉您是否可以访问互联网。
安装
npm install is-online
用法
import isOnline from 'is-online';
console.log(await isOnline());
//=> true
API
是否在线(选项?)
选项
类型:object
暂停
类型:number
默认:5000
等待服务器响应的毫秒数。
ip版本
类型:number
值:4 | 6
默认值:4
要使用的Internet 协议版本。
这是一个高级选项,通常不需要设置,但它对于专门声明 IPv6 连接很有用。
工作原理
以下检查同时运行:
- 通过 HTTPS检索icanhazip.com(或ipify.org作为后备)。
- 查询
myip.opendns.com
和o-o.myaddr.l.google.com
DNS 条目。 (仅限 Node.js) - 检索 Apple 的 Captive Portal 测试页面(这是 iOS 所做的)。 (仅限 Node.js)
当任何检查成功时,返回的 Promise 将被解析为true
。
代理支持
为了使其通过代理工作,您需要设置global-agent
。
源码解释
import got, {CancelError} from 'got'; // 从 'got' 模块导入 got 函数和 CancelError 类
import {publicIpv4, publicIpv6} from 'public-ip'; // 从 'public-ip' 模块中导入 publicIpv4 和 publicIpv6 函数
import pAny from 'p-any'; // 从 'p-any' 模块导入 pAny 函数
import pTimeout from 'p-timeout'; // 从 'p-timeout' 模块导入 pTimeout 函数
// appleCheck 函数:检测当前网络是否能访问 Apple 的测试网址
const appleCheck = options => {
// 发起请求到 'https://captive.apple.com/hotspot-detect.html',并传入超时时间和 IP 版本
const gotPromise = got('https://captive.apple.com/hotspot-detect.html', {
timeout: {
request: options.timeout,
},
dnsLookupIpVersion: options.ipVersion,
headers: {
'user-agent': 'CaptiveNetworkSupport/1.0 wispr', // 模拟 Apple 设备的 User-Agent
},
});
// 定义一个异步函数来处理请求的响应
const promise = (async () => {
try {
const {body} = await gotPromise;
if (!body?.includes('Success')) { // 如果响应内容不包含 'Success' 字符串,则抛出错误
throw new Error('Apple check failed');
}
} catch (error) {
if (!(error instanceof CancelError)) { // 如果错误不是 CancelError 类型,则抛出错误
throw error;
}
}
})();
// 将 gotPromise 的 cancel 方法赋值给 promise 对象的 cancel 方法
promise.cancel = gotPromise.cancel;
return promise;
};
// isOnline 函数:检测是否联网
// 不能是 `async` 函数,因为这样会失去 `.cancel()` 方法。
export default function isOnline(options) {
// 设置默认选项,并合并用户传入的 options
options = {
timeout: 5000,
ipVersion: 4,
...options,
};
// 如果所有的网络接口都是内部的,则直接返回 false
if (Object.values(os.networkInterfaces()).flat().every(({internal}) => internal)) {
return false;
}
// 如果 IP 版本不是 4 或 6,则抛出错误
if (![4, 6].includes(options.ipVersion)) {
throw new TypeError('`ipVersion` must be 4 or 6');
}
// 根据 IP 版本选择合适的 publicIpFunction 函数
const publicIpFunction = options.ipVersion === 4 ? publicIpv4 : publicIpv6;
const queries = []; // 保存所有查询的 Promise
// 使用 pAny 执行多个异步操作,返回第一个成功的结果
const promise = pAny([
(async () => {
const query = publicIpFunction(options);
queries.push(query); // 将查询加入 queries 数组
await query; // 等待查询完成
return true; // 如果查询成功,返回 true
})(),
(async () => {
const query = publicIpFunction({...options, onlyHttps: true});
queries.push(query);
await query;
return true;
})(),
(async () => {
const query = appleCheck(options);
queries.push(query);
await query;
return true;
})(),
]);
// 使用 pTimeout 设置超时,如果超时或出现错误,则取消所有查询并返回 false
return pTimeout(promise, {milliseconds: options.timeout}).catch(() => { // eslint-disable-line promise/prefer-await-to-then
for (const query of queries) {
query.cancel(); // 取消每个查询
}
return false; // 如果没有成功的查询,返回 false
});
// TODO: 在支持 AbortController 时使用这个替代方法。
// try {
// return await pTimeout(promise, options.timeout);
// } catch {
// for (const query of queries) {
// query.cancel();
// }
// return false;
// }
}