WiFi P2P简介

基本原理

WiFi Direct协议是Wi-Fi联盟发展、支持与授予认证的一套软件协议,也被称为Wi-Fi点对点(Wi-Fi Peer-to-Peer,简称P2P)。这套协议允许无线网络中的设备无需通过无线路由器即可相互连接,以点对点的方式直接与另一个 WiFi 设备连线,进行数据高速传输。

通常A与B通信是通过服务器转发数据。

P2P则可以实现A与B直接通信。

技术特点

  • 连接方式:允许 WiFi 设备直接连接,无需通过 AP,从而简化连接流程,提高了数据传输的便捷性。
  • 传输性能:在传输速度与传输距离方面比蓝牙有大幅度提升,最大传输距离可达 200 米,最大传输速度为 250Mbps。
  • 加密机制:支持 WPA2 加密机制,确保数据传输的安全性。

应用场景

  • 文件传输:可以在手机和平板之间快速传输照片、视频等大文件。
  • 内容共享:在会议中直接通过电脑展示手机内容,或在不同设备间共享应用。
  • 文档打印:直接连接打印机进行打印。
  • 数据同步:在设备间同步数据。
  • 娱乐应用:支持近距离的设备互联,适用于游戏、聊天、车机交互。

方案描述

通过wifiManager实现P2P连接,前提需要打开设备WLAN开关,整体方案分为建立P2P连接和P2P连接场景验证两个部分:

  • 建立P2P连接: 发现设备并与P2P设备建立连接。
  • P2P连接场景验证:验证P2P连接后的socket通信,模拟用户与车机交互。

建立P2P连接

1. 发现设备:P2P连接需要先发现P2P设备才能进行连接。

a. 注册发现设备状态监听。注册发现设备状态改变事件,注册事件可通过回调监听设备发现服务是否开启成功。

aboutToAppear(): void {
  // 注册发现设备状态监听
  wifiManager.on("p2pDiscoveryChange", this.recvP2pDiscoveryChangeFunc);
}

在业务退出时,要调用wifiManager.off('p2pDiscoveryChange')接口注销注册回调。

aboutToDisappear():
  // 注销发现设备状态监听
  wifiManager.off("p2pDiscoveryChange", this.recvP2pDiscoveryChangeFunc);
}

发现设备回调事件: 0- 初始状态。 1-发现成功。

// 发现设备状态改变事件。
recvP2pDiscoveryChangeFunc = (result: number) => {
  promptAction.showToast({
    message: result == 1 ? '发现设备成功' : '发现设备失败',
    duration: 2000
  });
}

开始发现设备,wifiManager.startDiscoverDevices接口为开启P2P发现设备关键步骤,发现设备状态结果通过监听事件回调。

Button('开始发现设备').onClick(() => {
  try {
    wifiManager.startDiscoverDevices();
  } catch (error) {
    console.error("wifiManager.startDiscoverDevices failed:" + JSON.stringify(error));
  }
})

    b. 获取设备列表。

    当发现设备阶段完成,即发现设备状态监听事件回调为1,此时设备可被p2p设备发现,且通过wifiManager.getP2pPeerDevices可获取对端设备列表。

    // 获取P2P对端设备列表信息
    getPeerDeviceList() {
      // p2p发现阶段完成,才能正常获取到对端设备列表信息
      this.peerDevices = []
      wifiManager.getP2pPeerDevices((err, data) => {
        if (err) {
          console.error("get P2P peer devices error");
          return;
        }
        console.info("get P2P peer devices: " + JSON.stringify(data));
        this.peerDevices = data
      });
    }

    2. 建立连接。

    a. 通过已发现设备WifiP2pDevice进行连接。

    建立p2p连接会有连接状态回调,通知连接状态变化,可根据状态变化实现对应逻辑。首先声明监听事件recvP2pConnectionChangeFunc。

    // p2p连接状态改变事件
    recvP2pConnectionChangeFunc = (result: wifiManager.WifiP2pLinkedInfo) => {
      console.info("p2p connection change receive event: " + JSON.stringify(result));
      if (result.connectState == 1) {
        promptAction.showToast({
          message: '已连接',
          duration: 2000
        });
      }
    }

    再通过wifiManager.on("p2pConnectionChange")注册p2p连接状态监听。

    aboutToAppear(): void {
      // 注册p2p连接状态监听
      wifiManager.on("p2pConnectionChange", this.recvP2pConnectionChangeFunc)
    }

    p2p连接状态监听在业务结束时通过wifiManager.off("p2pConnectionChange")注销。

    aboutToDisappear(): void {
      // 注销p2p连接状态监听
      wifiManager.off('p2pConnectionChange',this.recvP2pConnectionChangeFunc)
    }

    从扫描到的设备列表中,选择需要连接的设备通过wifiManager.p2pConnect进行P2P连接。

    // 建立对端连接
    connectToPeerDevice(device: wifiManager.WifiP2pDevice) {
      let config: wifiManager.WifiP2PConfig = {
        deviceAddress: device.deviceAddress,
        deviceAddressType: device.deviceAddressType,
        netId: 0,
        passphrase: "",
        groupName: "",
        goBand: 0,
      }
      try {
        wifiManager.p2pConnect(config);
      } catch (e) {
        console.error("wifiManager.p2pConnect error ", e.code);
      }
    }

    b. 获取P2P连接群组信息。

    p2p连接成功后获取当前群组信息,获取goIpAddress,通过goIpAddress可与对端服务进行通信。

    // 获取当前群组信息
    getGroupInfo(){
      wifiManager.getCurrentGroup((err, data) => {
        if (err) {
          console.error("get current P2P group error");
          return;
        }
        console.info("get current P2P group: " + JSON.stringify(data));
        this.goIpAddress = data.goIpAddress
      });
    }

    P2P连接场景验证:

    1,手机模拟车机系统。

    效果图:用户连接车机socket成功

    const tcpServer = socket.constructTCPSocketServerInstance()
    
    export function createServer(callback: Callback<boolean>): void {
      let ipAddress : socket.NetAddress = {} as socket.NetAddress
      ipAddress.address = "0.0.0.0"
      ipAddress.port = 4651
      tcpServer.listen(ipAddress, (err: BusinessError) => {
        if (err) {
          console.log("listen fail");
          return
        }
        console.log("listen success");
        promptAction.showToast({
          message: JSON.stringify('listen success'),
          duration: 2000
        })
      })
     
      class SocketInfo {
        message: ArrayBuffer = new ArrayBuffer(1);
        remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo;
      }
     
      // Listen client connect to server
      tcpServer.on("connect", (connection: socket.TCPSocketConnection) => {
        tcpConnectArray.pop()
        tcpConnectArray.push(connection)
     
        // Listen connection closed
        connection.on("close", () => {
          console.info("on close success")
        });
     
        // Listen receive message
        connection.on("message", (value: SocketInfo) => {
          let buffer = value.message
          let dataView = new DataView(buffer);
          let str = ""
          for (let i = 0; i < dataView.byteLength; ++i) {
            str += String.fromCharCode(dataView.getUint8(i))
          }
          // Response to client
          response()
          // Toast message
          promptAction.showToast({
            message: JSON.stringify(str),
            duration: 2000
          })
        })
     
        // Send data to the client for Connection state.
        let tcpSendOptions : socket.TCPSendOptions = {} as socket.TCPSendOptions
        tcpSendOptions.data = 'Hello, client, connect success!'
        connection.send(tcpSendOptions, (err: BusinessError) => {
          if (err) {
            console.log("send fail")
            return
          }
          console.log("send success")
        })
      })
    }

    2,手机应用连接车机服务

    效果图:连接车机socket成功

    通过p2p连接获取到的goIpAddress连接车机Socket服务:

    // 连接车机Socket服务
    createSocketClient(){
      let ipAddress: socket.NetAddress = {} as socket.NetAddress;
      ipAddress.address = this.goIpAddress;
      ipAddress.port = 4651;
      let tcpConnect: socket.TCPConnectOptions = {} as socket.TCPConnectOptions;
      tcpConnect.address = ipAddress;
      tcpConnect.timeout = 6000;
      tcp.connect(tcpConnect, (err: BusinessError) => {
        if (err) {
          console.log('connect fail' + JSON.stringify(err));
          return;
        }
        console.log('connect success');
      });
    }

    3,手机应用向车机服务发送消息

    效果图:车机收到用户发送的‘hello,my car!’

    // 发送消息
    let clientSendOptions: socket.TCPSendOptions = {} as socket.TCPSendOptions;
    export function sendMsg() {
      clientSendOptions.data = 'hello,my car!';
      tcp.send(clientSendOptions, (err: BusinessError) => {
        if (err) {
          console.log('sendMsg fail ');
          return;
        }
        console.warn('fxm debug : client sendMsg success');
      })
    }

    4,车机服务响应手机应用消息

    效果图:用户收到车机响应的‘hello,have a good day!’

    在创建的车机socket server中,有接受消息的监听,监听到手机应用请求后,做出响应。

    export function response() {
      let connection: socket.TCPSocketConnection = tcpConnectArray[0];
      let tcpSendOptions: socket.TCPSendOptions = {} as socket.TCPSendOptions
      tcpSendOptions.data = 'hello,have a good day!'
      connection.send(tcpSendOptions, (err: BusinessError) => {
        if (err) {
          console.log("response fail")
          return
        }
        console.log("response success")
      })
    }

    5,车机服务向手机应用推送消息

    效果图:用户收到车机推送的‘hello, the car has reached its destination!’

    车机系统也可向手机应用主动推送消息。

    let tcpSendOptions : socket.TCPSendOptions = {} as socket.TCPSendOptions
    tcpSendOptions.data = 'hello, the car has reached its destination!'
    connection.send(tcpSendOptions, (err: BusinessError) => {
      if (err) {
        console.log("send fail")
        return
      }
      console.log("send success")
    })

    到此则实现p2p连接后的socket通信验证。

    Logo

    讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

    更多推荐