HarmonyOS-ArkUI NetConnection基本使用

发布于:2025-04-03 ⋅ 阅读:(19) ⋅ 点赞:(0)

网络连接管理包含项目

  • 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));
});