网络连接管理包含项目
- WIFI, 蜂窝,以太,等网络的连接优先级管理
- 网络质量评估
- 订阅默认网络,或者制定网络的状态变化
- 查询网络连接信息
- DNS解析
- ...
订阅网络变化
对象关系概览
订阅网络变化,首先要获取一个NetConnection实例。在这里我们有必要对NetConnection这个结构关系铺垫一下。 本文主要涉及的就两个类, NetConnection 和 NetHandler类。 两者的关系是
- NetConnection是网络连接的总入口,可以通过这个NetConnection获取多个NetHandler。
- NetConnection相当于网络管理员,NetHandler相当于网络接口操作员
- 通常通过NetConnection获得网络信息。全局的信息或者默认的信息。 然后再通过NetHandler执行具体的操作。
而对于NetConnection对于管理能力,查询能力,感知能力的实现。一些操作需要它的静态接口来完成,而对于实例级操作,才会涉及到初始化NetConnection实例,然后调用实例接口来完成。
对于NetConnection的静态操作和对象级别操作如图所示。我们只需要记住,NetConnection存在模块级操作,和实例级操作,以及对应接口有个大概即可。我们接下来的代码,您会发现有时候需要用到NetConnection实例,有时候不需要只调用静态接口即可。 如果心里没有一个分类区别,写代码的时候会产生疑惑。
订阅网络状态变化
{
"module": {
...
"requestPermissions": [
{"name": 'ohos.permission.INTERNET'},
{"name": 'ohos.permission.GET_NETWORK_INFO'}
],
...
}
}
import { connection } from '@kit.NetworkKit'
import { BusinessError } from '@kit.BasicServicesKit';
import { JSON } from '@kit.ArkTS';
export default class entryAbility extends UIAbility {
private mNetConnect?: connection.NetConnection
private mDefaultNetHandle?: connection.NetHandle
...
registerNetworkStatus(){
// 首先初始化一个NetConnection实例
this.mNetConnect = connection.createNetConnection()
this.mNetConnect.register(()=>{
hilog.info(0x0000, 'testTag', "netConnection register callback! success!")
})
// log print info: handle={"netId":100}
this.mNetConnect.on('netAvailable', (handle)=> {
hilog.info(0x0000, 'testTag', "net status callback: (netAvailable): handle=" + JSON.stringify(handle))
})
this.mNetConnect.on('netUnavailable', ()=> {
hilog.info(0x0000, 'testTag', "net status callback: (netUnavailable)")
})
this.mNetConnect.on('netLost', (handle) => {
hilog.info(0x0000, 'testTag', "net status callback: (netLost): handle=" + JSON.stringify(handle))
})
this.mNetConnect.on('netBlockStatusChange', (netBlockStatusInfo) => {
hilog.info(0x0000, 'testTag', "net status callback: (netBlockStatusChange): netBlockStatusInfo=" + JSON.stringify(netBlockStatusInfo))
})
// log print info: netCapabilityInfo={"netHandle":{"netId":100},"netCap":{"linkUpBandwidthKbps":0,"linkDownBandwidthKbps":0,"networkCap":[12,15,16],"bearerTypes":[1]}}
this.mNetConnect.on('netCapabilitiesChange', (netCapabilityInfo)=> {
hilog.info(0x0000, 'testTag', "net status callback: (netCapabilitiesChange): netCapabilityInfo=" + JSON.stringify(netCapabilityInfo))
})
// log print info: netConnectionPropertyInfo={"netHandle":{"netId":100},"connectionProperties":{"interfaceName":"wlan0","domains":"","mtu":0,"linkAddresses":[{"address":{"address":"192.168.0.105","family":1,"port":0},"prefixLength":24}],"routes":[{"interface":"wlan0","destination":{"address":{"address":"0.0.0.0","family":1,"port":0},"prefixLength":0},"gateway":{"address":"192.168.0.1","prefixLength":0},"hasGateway":true,"isDefaultRoute":false}],"dnses":[{"address":"192.168.1.1","family":1,"port":0},{"address":"192.168.0.1","family":1,"port":0}]}}
this.mNetConnect.on('netConnectionPropertiesChange', (netConnectionPropertyInfo) => {
hilog.info(0x0000, 'testTag', "net status callback: (netConnectionPropertiesChange): netConnectionPropertyInfo=" + JSON.stringify(netConnectionPropertyInfo))
})
// 当您再也不需要这些状态的时候, 请调用this.mNetConnect.unregister
}
}
网络状态详解
上方为网络状态的订阅及取消订阅实现。对于这些网络事件我们做下了解:
常见网络状态事件订阅场景
事件订阅API
1 netAvaliable
网络可用事件, API为
netConnection.on(type: 'netAvailable', callback: CallBack<NetHandle>) : void
2 netUnavaliable
netConnection.on(type: 'netUnavaliable', callback: CallBack<NetHandle>) : void
3 netBlockStatusChange
网络阻塞事件。当网络连接超时,传输的数据包丢失,或者网络带宽不足,负载过高时触发。
netConnection.on(type: 'netBlockStatusChange', callBack: Callback<NetBlockStatusInfo>) : void
- NetLockStatusInfo含有两个字段
-
- netHandle: NetHandle 数据网络句柄
- blocked: boolean 标识当前网络是否阻塞
4 netCapabilitiesChange
网络能力事件变化,当网络类型或者带宽等发生变化时触发。
netConnection.on(type: 'netCapabilitiesChange', callBack: CallBack<NetCapabilityInfo>) : void
NetCapabilityInfo 含有两个字段
- netHandle: NetHandle 数据网络句柄
- netCap:NetCapabilities 网络能力集
5 netConnectionPropertiesChange
网络连接信息变化事件, 当IP地址,网关等信息发生变化时回触发。
netConnection.on(type: 'netConnectionPeopertiesChange', callBack: CallBack<NetConnectionPropertiesInfo>) : void
NetConnectionPropertiesInfo 含有两个字段
- netHandle:NetHandle 数据网络句柄
- connectionProperties: ConnectionProperties 网络连接属性
-
- interFaceName: 网卡名称
- domains: 域名
- linkAddress: 链路信息
- routes: 路由信息
- dness: 网络地址
- mtu: 最大传输单元
6 onLost
当无网络状态,或者网路不可用的时候,会触发网络丢失事件。
netConnection.on(type: 'onLost', callback: CallBack<void>) : void
NetConnection实例级操作
获取NetConnection实例
获取默认激活网络及能力
什么是默认激活网络
默认激活网络指的是,系统优先使用的,正常的网络。系统自动切换。
对于系统自动切换的网络,有以下三个维度
- 物理层的网络已经连接上
- 具备互联网访问能力
- 具备优先级方面,依次如下
-
- 有线网络
- wifi
- 蜂窝
- VPN
- 其他类型网络
以上是系统选择网络的策略。因为以上策略,再加上我们所处的环境不断变动。所以这个默认激活网络具备变动性
当更高优先级的网络出现时,连接成功的时候会自动切换网络。
至少保持TCP/IP协议栈的ESTABLISHED状态
获取默认激活网络
获取默认激活网络的API是:connection下的
function getDefaultNet(callback: AsyncCallback<NetHandle>): void;
NetHandle
是数据网络的句柄,我们可以拿这个句柄继续获取数据网络的能力
获取默认激活网络的能力集
connection下的:
function getNetCapabilities(netHandle: NetHandle): Promise<NetCapabilities>;
import { connection } from '@kit.NetworkKit'
...
export default class entryAbility extends UIAbility {
private mNetConnect?: connection.NetConnection;
...
某函数() {
// 第一种实例获取方式, 关注的是默认网络
this.mNetConnect = connection.createNetConnection();
}
}
获取指定要求的NetConnection实例
创建函数声明如下
connection.createNetConnection(netSpecifier?: NetSpecifier, timeout?: number) : NetConnection
使用方法
import { connection } from '@kit.NetworkKit'
...
export default class entryAbility extends UIAbility {
private mNetConnect?: connection.NetConnection;
...
某函数() {
// 第二种实例获取方式,关注的是蜂窝网络
this.mNetConnect = connection.createNetConnection(
{netCapabilities:{
bearerTypes: [connection.NetBearType.BEARER_CELLULAR] //关注蜂窝网络
}}
,0)
}
}
通用场景及NetConnection接口使用
网络连接管理的典型应用场景为
- 获取所有注册网络。
- 接收指定网络的变化通知。
- 根据数据网络查询网络的连接信息。
- 使用对应网络解析域名,获取所有IP。
上述我们已经获得了一个NetConnection的实例。接下来我们拿这个实例调用相关接口,就可以完成上述的场景。
接收网络变化通知
前文已经讲到了。订阅网络状态变化 小节有描述。
监控默认网络变化,并主动重建网络连接
对于默认网络,我们之前提到了默认网络切换的优先级规则。这就导致,默认网络会根据所处网络环境的不同,而不断地进行切换。因为切换的过程中您可能此时正进行请求中。所以为了确保正确的数据传输,我们需要做一下兼容。
监控默认网络变化
// 首先初始化一个NetConnection实例
this.mNetConnect = connection.createNetConnection()
this.mNetConnect.register(()=>{
hilog.info(0x0000, 'testTag', "netConnection register callback! success!")
})
// log print info: handle={"netId":100}
this.mNetConnect.on('netAvailable', (handle)=> {
hilog.info(0x0000, 'testTag', "net status callback: (netAvailable): handle=" + JSON.stringify(handle))
})
默认网络变化后重新建立连接
原网络使用http模块建立了网络连接
如果您已经使用了http模块建立了链接,由于该模块并没有提供close接口便于关闭socket,在切换默认网络并建立新的连接的时候,原有的Socket并没有关闭。因此请使用Remote Communication kit来建立链接。
import { rcp } from '@kit.RemoteCommunicationKit';
import { connection } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
let session = rcp.createSession();
async function useRcp() {
/* 建立rcp请求。 */
try {
const request = await session.get('https://www.example.com');
console.info(request.statusCode.toString());
} catch (e) {
console.error(e.code.toString());
}
}
async function rcpTest() {
const netConnection = connection.createNetConnection();
netConnection.on('netAvailable', async (netHandle: connection.NetHandle) => {
/* 发生默认网络切换,重新建立session。 */
session.close();
session = rcp.createSession();
useRcp();
});
try {
netConnection.register(() => {
});
useRcp();
} catch (e) {
console.error(e.code.toString());
}
}
原网络连接使用socket模块建立连接
import { connection, socket } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
let sock: socket.TCPSocket = socket.constructTCPSocketInstance();
async function useSocket() {
let tcpConnectOptions: socket.TCPConnectOptions = {
address: {
address: '192.168.xx.xxx',
port: 8080
},
timeout: 6000
}
/* 建立socket连接。 */
sock.connect(tcpConnectOptions, (err: BusinessError) => {
if (err) {
console.error('connect fail');
return;
}
console.log('connect success');
/* 通过socket发送数据。 */
let tcpSendOptions: socket.TCPSendOptions = {
data: 'Hello, server!'
}
sock.send(tcpSendOptions).then(() => {
console.log('send success');
}).catch((err: BusinessError) => {
console.error('send fail');
});
})
}
async function socketTest() {
const netConnection = connection.createNetConnection();
netConnection.on('netAvailable', async (netHandle: connection.NetHandle) => {
console.log('default network changed');
await sock.close();
sock = socket.constructTCPSocketInstance();
useSocket();
});
try {
netConnection.register(() => {
});
useSocket();
} catch (e) {
console.error(e.code.toString());
}
}
获取所有注册的网络
this.mNetConnect = connection.createNetConnection()
connection.getAllNets((error : BusinessError, data) => {
// 如果获取成功.error为undefined
hilog.info(0x0000, 'testTag', "getAllNets callback! error=" + JSON.stringify(error) + " net length=" + data.length)
// log print info: handle 0 info={"netId":100}
for (let i = 0; i < data.length; i++) {
let netHd = data[i]
hilog.info(0x0000, 'testTag', "handle " + i + " info=" + JSON.stringify(netHd))
}
})
根据数据网络查询网络的能力信息及连接信息
import { connection } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 构造单例对象。
export class GlobalContext {
public netList: connection.NetHandle[] = [];
public netHandle: connection.NetHandle|null = null;
private constructor() {}
private static instance: GlobalContext;
private _objects = new Map<string, Object>();
public static getContext(): GlobalContext {
if (!GlobalContext.instance) {
GlobalContext.instance = new GlobalContext();
}
return GlobalContext.instance;
}
getObject(value: string): Object | undefined {
return this._objects.get(value);
}
setObject(key: string, objectClass: Object): void {
this._objects.set(key, objectClass);
}
}
// 调用getDefaultNet方法,获取默认的数据网络(NetHandle)。
connection.getDefaultNet().then((data:connection.NetHandle) => {
if (data.netId == 0) {
// 当前无默认网络时,获取的netHandler的netid为0,属于异常情况,需要额外处理。
return;
}
if (data) {
console.info("getDefaultNet get data: " + JSON.stringify(data));
GlobalContext.getContext().netHandle = data;
// 获取netHandle对应网络的能力信息。能力信息包含了网络类型、网络具体能力等网络信息。
connection.getNetCapabilities(GlobalContext.getContext().netHandle).then(
(data: connection.NetCapabilities) => {
console.info("getNetCapabilities get data: " + JSON.stringify(data));
// 获取网络类型(bearerTypes)。
let bearerTypes: Set<number> = new Set(data.bearerTypes);
let bearerTypesNum = Array.from(bearerTypes.values());
for (let item of bearerTypesNum) {
if (item == 0) {
// 蜂窝网络。
console.log(JSON.stringify("BEARER_CELLULAR"));
} else if (item == 1) {
// Wi-Fi网络。
console.log(JSON.stringify("BEARER_WIFI"));
} else if (item == 3) {
// 以太网网络。
console.log(JSON.stringify("BEARER_ETHERNET"));
}
}
// 获取网络具体能力(networkCap)。
let itemNumber : Set<number> = new Set(data.networkCap);
let dataNumber = Array.from(itemNumber.values());
for (let item of dataNumber) {
if (item == 0) {
// 表示网络可以访问运营商的MMSC(Multimedia Message Service,多媒体短信服务)发送和接收彩信。
console.log(JSON.stringify("NET_CAPABILITY_MMS"));
} else if (item == 11) {
// 表示网络流量未被计费。
console.log(JSON.stringify("NET_CAPABILITY_NOT_METERED"));
} else if (item == 12) {
// 表示该网络应具有访问Internet的能力,该能力由网络提供者设置。
console.log(JSON.stringify("NET_CAPABILITY_INTERNET"));
} else if (item == 15) {
// 表示网络不使用VPN(Virtual Private Network,虚拟专用网络)。
console.log(JSON.stringify("NET_CAPABILITY_NOT_VPN"));
} else if (item == 16) {
// 表示该网络访问Internet的能力被网络管理成功验证,该能力由网络管理模块设置。
console.log(JSON.stringify("NET_CAPABILITY_VALIDATED"));
}
}
})
}
});
// 获取netHandle对应网络的连接信息。连接信息包含了链路信息、路由信息等。
connection.getConnectionProperties(GlobalContext.getContext().netHandle).then((data: connection.ConnectionProperties) => {
console.info("getConnectionProperties get data: " + JSON.stringify(data));
})
// 调用getAllNets,获取所有处于连接状态的网络列表(Array<NetHandle>)。
connection.getAllNets().then((data: connection.NetHandle[]) => {
console.info("getAllNets get data: " + JSON.stringify(data));
if (data) {
GlobalContext.getContext().netList = data;
let itemNumber : Set<connection.NetHandle> = new Set(GlobalContext.getContext().netList);
let dataNumber = Array.from(itemNumber.values());
for (let item of dataNumber) {
// 循环获取网络列表每个netHandle对应网络的能力信息。
connection.getNetCapabilities(item).then((data: connection.NetCapabilities) => {
console.info("getNetCapabilities get data: " + JSON.stringify(data));
})
// 循环获取网络列表每个netHandle对应的网络的连接信息。
connection.getConnectionProperties(item).then((data: connection.ConnectionProperties) => {
console.info("getConnectionProperties get data: " + JSON.stringify(data));
})
}
}
})
网络信息数据结构
使用对应网络解析域名获取IP
// 引入包名。
import { connection } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 使用默认网络解析主机名以获取所有IP地址。
connection.getAddressesByName("xxxx").then((data: connection.NetAddress[]) => {
console.info("Succeeded to get data: " + JSON.stringify(data));
});