HarmonyOS技术精讲-Connectivity Kit:Wi-Fi进阶能力——热点、P2P与Aware

一、这个功能解决什么问题

针对设备互联的场景,HarmonyOS 提供了三个不同级别的 Wi-Fi 进阶能力:

能力 适用场景 是否需要路由器 典型延迟
Wi-Fi 热点 设备共享网络、临时组网 否(设备自身模拟) 高,类似普通 AP
Wi-Fi P2P 高速文件传输、投屏 否(点对点直连) 低,峰值可达数百 Mbps
Wi-Fi Aware 邻近发现、轻量匹配 否(设备间直接感知) 极低,主要用于发现

核心原则:能用 Aware 发现,就用 Aware;发现后需要传输,切换到 P2P;仅需要共享网络,开热点。

这个设计思路很清晰,但实际开发中有不少隐藏的约束,下面逐一展开。


二、环境说明

text 复制代码
DevEco Studio 版本:DevEco Studio 6.1.0 及以上
HarmonyOS SDK 版本:HarmonyOS 6.1.0 (23) 及以上
目标设备:手机(需支持 Wi-Fi 6 或 Wi-Fi Aware 功能)

三、Wi-Fi 热点 ------ 让设备成为 AP

1. 核心 API

typescript 复制代码
import { wifiManager } from '@kit.ConnectivityKit';

创建热点有两个关键 API:enableHotspotdisableHotspot

很多人在升级到 API 11 后,发现传入的参数和旧版本不一样了。新版本要求传入 HotspotConfig 结构体,并且需要先获取 wifiManager 实例,不再像以前那样直接调用静态方法。

2. 完整示例代码

typescript 复制代码
// HotspotDemo.ets
import { wifiManager } from '@kit.ConnectivityKit';
import { common } from '@kit.AbilityKit';

@Entry
@Component
struct HotspotDemo {
  @State isHotspotOn: boolean = false;
  @State statusText: string = '热点未开启';

  build() {
    Column({ space: 16 }) {
      Text(this.statusText)
        .fontSize(16)
        .margin({ bottom: 16 })

      Button(this.isHotspotOn ? '关闭热点' : '开启热点')
        .onClick(async () => {
          if (this.isHotspotOn) {
            await this.disableHotspot();
          } else {
            await this.enableHotspot();
          }
        })
    }
    .padding(24)
    .width('100%')
    .height('100%')
  }

  // 开启热点
  private async enableHotspot(): Promise<void> {
    try {
      // 检查是否已开启
      const isActive: boolean = wifiManager.isHotspotActive();
      if (isActive) {
        this.statusText = '热点已经开启';
        this.isHotspotOn = true;
        return;
      }

      // 构造热点配置
      const config: wifiManager.HotspotConfig = {
        ssid: 'HarmonyOS_Hotspot',      // 自定义 SSID
        securityType: wifiManager.SecurityType.WPA2_PSK,  // 安全类型
        preSharedKey: '12345678',        // 密码,至少8位
        bandwidth: wifiManager.Bandwidth.BANDWIDTH_2_4_G,
        channel: 1,                      // 可选,不传系统自动分配
      };

      const success: boolean = await wifiManager.enableHotspot(config);
      if (success) {
        this.isHotspotOn = true;
        this.statusText = '热点已开启,SSID: ' + config.ssid;
      } else {
        this.statusText = '开启失败,请检查权限或系统限制';
      }
    } catch (err) {
      this.statusText = '异常: ' + JSON.stringify(err);
    }
  }

  // 关闭热点
  private async disableHotspot(): Promise<void> {
    try {
      const success: boolean = await wifiManager.disableHotspot();
      if (success) {
        this.isHotspotOn = false;
        this.statusText = '热点已关闭';
      }
    } catch (err) {
      this.statusText = '关闭异常: ' + JSON.stringify(err);
    }
  }
}

3. 注意事项

  • 密码必须不少于 8 位 ,否则 enableHotspot 直接返回 false。
  • securityType 不支持 OPEN 时,必须指定 WPA2 或 WPA3。
  • 开启热点后,设备自身的 Wi-Fi 连接会被断开,这是硬件限制。
  • 模拟器上不支持热点功能,必须真机测试。

四、Wi-Fi P2P ------ 高速直连传输

P2P 的主要流程:发现对端 -> 创建组或连接 -> 传输数据。

1. 关键 API

typescript 复制代码
import { p2pManager } from '@kit.ConnectivityKit';

需要关注的几个方法:

  • p2pManager.createGroup(iface) ------ 创建 P2P 组(设备作为组所有者)
  • p2pManager.connectToDevice(iface, deviceConfig) ------ 连接特定设备
  • p2pManager.stopPeerDiscovery(iface) ------ 停止发现

2. 完整 P2P 连接示例

typescript 复制代码
// P2PDemo.ets
import { p2pManager, wifiManager } from '@kit.ConnectivityKit';

interface WifiP2pDevice {
  deviceName: string;
  deviceAddress: string;
}

@Entry
@Component
struct P2PDemo {
  @State discoveredDevices: WifiP2pDevice[] = [];
  @State connectionStatus: string = '未连接';

  // P2P 监听器
  private listener: p2pManager.P2pListener | null = null;

  aboutToAppear() {
    this.registerP2pListener();
  }

  aboutToDisappear() {
    this.unregisterP2pListener();
  }

  build() {
    Column({ space: 12 }) {
      Text('P2P 直连')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

      Text(this.connectionStatus)
        .fontSize(14)
        .margin({ bottom: 16 })

      Button('发现对端')
        .onClick(() => this.discoverPeers())

      List({ space: 8 }) {
        ForEach(this.discoveredDevices, (device: WifiP2pDevice, index: number) => {
          ListItem() {
            Row() {
              Text(device.deviceName)
                .fontSize(16)
              Button('连接')
                .onClick(() => {
                  this.connectToDevice(device.deviceAddress);
                })
            }
            .justifyContent(FlexAlign.SpaceBetween)
            .padding(12)
            .border({ width: 1, color: Color.Gray })
          }
        })
      }
      .width('100%')
      .height(300)

      Button('创建组(作为所有者)')
        .onClick(() => this.createGroup())
    }
    .padding(24)
    .width('100%')
  }

  // 注册 P2P 事件监听
  private registerP2pListener(): void {
    const iface = 'wlan0'; // 大部分设备接口名
    this.listener = {
      onP2pStateChanged: (state: p2mManager.P2pState) => {
        console.info('P2P state changed:', state);
      },
      onP2pDevicesFound: (devices: p2pManager.WifiP2pDevice[]) => {
        // 发现设备列表更新
        this.discoveredDevices = devices.map(d => ({
          deviceName: d.deviceName,
          deviceAddress: d.deviceAddress,
        }));
        console.info('Found devices:', this.discoveredDevices.length);
      },
      onP2pConnectionChanged: (info: p2pManager.P2pInfo) => {
        if (info.groupFormed && info.isGroupOwner) {
          this.connectionStatus = '作为群主,已连接';
        } else if (info.groupFormed) {
          this.connectionStatus = '已连接至群主';
        } else {
          this.connectionStatus = '未连接';
        }
      },
    };

    p2pManager.on('p2pStateChange', this.listener);
    p2pManager.on('p2pDeviceChange', this.listener);
    p2pManager.on('p2pConnectionChange', this.listener);
  }

  private unregisterP2pListener(): void {
    if (this.listener) {
      p2pManager.off('p2pStateChange', this.listener);
      p2pManager.off('p2pDeviceChange', this.listener);
      p2pManager.off('p2pConnectionChange', this.listener);
      this.listener = null;
    }
  }

  private async discoverPeers(): Promise<void> {
    try {
      const iface = 'wlan0';
      const found = await p2pManager.discoverPeers(iface, 10_000); // 超时10秒
      if (found) {
        console.info('Discovery started, waiting for devices...');
      }
    } catch (err) {
      console.error('Discovery failed:', err);
    }
  }

  private async connectToDevice(address: string): Promise<void> {
    try {
      const iface = 'wlan0';
      const config: p2pManager.DeviceConfig = {
        deviceAddress: address,
        groupOwnerIntent: 3, // 0-15,数值越小越倾向于作为客户端,越大越作为群主
        networkId: -1,       // -1 表示任意网络
        passphrase: '12345678', // 群主需要设置的密码
      };
      const success = await p2pManager.connectToDevice(iface, config);
      if (success) {
        this.connectionStatus = '连接发起成功,等待对方确认';
      }
    } catch (err) {
      console.error('Connection failed:', err);
    }
  }

  private async createGroup(): Promise<void> {
    try {
      const iface = 'wlan0';
      const config: p2pManager.GroupConfig = {
        networkName: 'P2P-Group',
        passphrase: '87654321',
      };
      const success = await p2pManager.createGroup(iface, config);
      if (success) {
        this.connectionStatus = 'P2P 组创建成功,等待客户端连接';
      }
    } catch (err) {
      console.error('Create group failed:', err);
    }
  }
}

3. 核心注意事项

  • P2P 连接成功后,设备之间的通信可以通过 Socket 进行,建议使用 TCP 或 UDP。
  • groupOwnerIntent 决定了优先级,如果两个设备都想当群主,会协商失败。
  • 监听器的注册要在 aboutToAppear 中完成,并在 aboutToDisappear 中注销,避免页面销毁后回调导致的状态更新。

五、Wi-Fi Aware ------ 设备发现与轻量通信

Aware 的核心思路:设备在不连接 AP 的情况下,通过超低功耗发现邻近服务。

1. 关键 API

aware.subscribe 订阅服务,aware.publish 发布服务。

2. 完整示例

typescript 复制代码
// AwareDemo.ets
import { aware } from '@kit.ConnectivityKit';

@Entry
@Component
struct AwareDemo {
  @State serviceStatus: string = '未发布/订阅';
  @State foundServices: string[] = [];

  private serviceId: number | null = null;

  build() {
    Column({ space: 16 }) {
      Text(this.serviceStatus)
        .fontSize(14)

      Button('发布服务')
        .onClick(() => this.publishService())

      Button('订阅周围服务')
        .onClick(() => this.subscribeService())

      List() {
        ForEach(this.foundServices, (service: string, index: number) => {
          ListItem() {
            Text('发现: ' + service)
          }
        })
      }
      .width('100%')
      .height(200)
    }
    .padding(24)
  }

  private async publishService(): Promise<void> {
    try {
      // 发布一个本地服务
      const config: aware.PublishConfig = {
        serviceName: 'HarmonyOS Demo Service',
        serviceSpecificInfo: new Uint8Array([0x01, 0x02, 0x03]), // 自定义信息
        matchFilter: new Uint8Array([0x01]), // 过滤条件
        publishType: aware.PublishType.UNSOLICITED, // 主动发布
        ttlSeconds: 300, // 存活时间
      };

      const publishId = await aware.publish(config);
      if (publishId !== null) {
        this.serviceId = publishId;
        this.serviceStatus = '服务已发布, ID: ' + publishId;
      }
    } catch (err) {
      this.serviceStatus = '发布失败: ' + err.message;
    }
  }

  private async subscribeService(): Promise<void> {
    try {
      const config: aware.SubscribeConfig = {
        serviceName: 'HarmonyOS Demo Service', // 只订阅同名服务,不传则所有
        matchFilter: new Uint8Array([0x01]),
        subscribeType: aware.SubscribeType.ACTIVE, // 主动轮询
      };

      // 订阅回调
      const subscribeId = await aware.subscribe(config, (discoveredServices: aware.DiscoveredService[]) => {
        // 每次发现更新都会触发
        this.foundServices = discoveredServices.map(s => s.serviceName);
        console.info('Aware services found:', this.foundServices);
      });

      if (subscribeId !== null) {
        this.serviceStatus = '订阅成功, ID: ' + subscribeId;
      }
    } catch (err) {
      this.serviceStatus = '订阅失败: ' + err.message;
    }
  }
}

3. 坑点说明

  • Aware 需要设备硬件支持,目前仅部分中高端机型具备。
  • publishsubscribematchFilter 必须匹配,否则两端无法发现对方。
  • 订阅的回调是异步的,且频率可能较高,不要在回调里做耗时操作或者频繁 setState。

六、综合场景:P2P 文件传输

结合 Aware 发现 + P2P 传输,实现一个文件共享场景。

流程:

  1. A 设备发布 Aware 服务 (表示"我可共享文件")
  2. B 设备订阅 Aware 服务,发现 A
  3. B 通过 A 的 Aware 信息获取 IP/端口,切换到 P2P 连接
  4. P2P 建立后,通过 Socket 传输文件

由于篇幅,这里给出简化版的 P2P 传输代码,Aware 发现部分参照上一节。

typescript 复制代码
// FileTransferDemo.ets
import { socket } from '@kit.NetworkKit';
import { p2pManager } from '@kit.ConnectivityKit';

@Entry
@Component
struct FileTransferDemo {
  @State transferStatus: string = '等待连接';

  private serverSocket: socket.TCPSocket | null = null;
  private clientSocket: socket.TCPSocket | null = null;

  build() {
    Column({ space: 16 }) {
      Text(this.transferStatus)
        .fontSize(16)

      Button('启动服务端(接收文件)')
        .onClick(() => this.startServer())

      Button('连接并发送数据')
        .onClick(() => this.connectAndSend())
    }
    .padding(24)
  }

  // 作为服务端监听连接
  private async startServer(): Promise<void> {
    try {
      this.serverSocket = new socket.TCPSocket();
      // 绑定本地端口,注意 P2P 接口通常是 192.168.49.x
      await this.serverSocket.bind({ address: '0.0.0.0', port: 8888, family: 1 });
      await this.serverSocket.listen(1); // 最多接受一个连接

      this.serverSocket.on('message', (data: ArrayBuffer) => {
        // 收到数据,简单转成字符串
        const decoder = new util.TextDecoder('utf-8');
        const text = decoder.decodeToString(new Uint8Array(data));
        this.transferStatus = '接收到: ' + text;
      });

      this.transferStatus = '服务端已启动,等待连接...';
    } catch (err) {
      this.transferStatus = '启动服务端失败: ' + err.message;
    }
  }

  // 作为客户端连接并发送
  private async connectAndSend(): Promise<void> {
    try {
      // 实际项目中,这个 address 来自 Aware 发现或 P2P 连接后的 IP
      const P2P_SERVER_IP = '192.168.49.1'; // 群主的典型 IP
      const PORT = 8888;

      this.clientSocket = new socket.TCPSocket();
      await this.clientSocket.connect({ address: P2P_SERVER_IP, port: PORT, family: 1 });

      // 发送一个字符串(实际文件可用流式发送)
      const encoder = new util.TextEncoder();
      const data = encoder.encodeInto('Hello from HarmonyOS P2P!');
      await this.clientSocket.send(data);
      
      this.transferStatus = '数据发送成功';
      this.clientSocket.close();
    } catch (err) {
      this.transferStatus = '发送失败: ' + err.message;
    }
  }
}

常见问题

Q:为什么 P2P 发现不到设备?

可能是两台设备未在同一信道上。建议先开启 Wi-Fi 扫描,让两者都处于扫描状态。此外,部分定制 ROM 限制了 P2P 功能。

Q:Aware 在模拟器上能跑吗?

不能。Aware 依赖硬件支持,模拟器无法模拟射频信号。必须使用真机测试,并且该机型需支持 Wi-Fi Aware。

Q:开启热点后,为什么 P2P 无法使用?

同设备上热点和 P2P 无法共存。如果你正在使用热点分享网络,P2P 功能会被系统限制,需要先关闭热点。

Q:P2P 连接成功后,如何获取对方的 IP 地址?

可以通过 p2pManager.getP2pInfo 获取,返回的信息中包含 groupOwner 的 IP 地址,客户端地址需要从 Socket 连接中获取。


相关推荐
Helen_cai6 小时前
HarmonyOS ArkTS 实战:实现一个校园宿舍管理与查寝应用
华为·harmonyos
listening7776 小时前
HarmonyOS ArkTS 实战:实现一个校园打印复印预约应用
华为·harmonyos
绝世番茄7 小时前
鸿蒙原生 ArkTS 布局之道:Grid 替代多层嵌套布局深度解析
华为·harmonyos·鸿蒙
●VON7 小时前
鸿蒙 PC Markdown 编辑器可靠性设计:未保存内容的崩溃恢复闭环
华为·中间件·编辑器·harmonyos·鸿蒙
●VON8 小时前
鸿蒙 PC Markdown 编辑器导出架构:安全 HTML 与系统 PDF
华为·架构·编辑器·harmonyos·鸿蒙
不羁的木木8 小时前
HarmonyOS技术精讲-Connectivity Kit(短距通信服务):初识统一短距通信框架
华为·wpf·harmonyos
爸爸6198 小时前
UIAbility 生命周期在日记 App 中的实践
后端·华为·harmonyos·鸿蒙系统
tyqtyq2216 小时前
旅行打包清单 App — HarmonyOS AI 应用开发技术博客
人工智能·学习·华为·生活·harmonyos
HONG````16 小时前
Lazy Import 动态加载:减少 HarmonyOS 应用冷启动时间
harmonyos