Android Wifi P2P 入门 |
您所在的位置:网站首页 › wlan直连需要打开热点吗 › Android Wifi P2P 入门 |
学习资料: Demo 下载地址:https://github.com/mengzhinan/WiFi_P2P_test GoogleAndroid Doc:https://developer.android.google.cn/guide/topics/connectivity/wifip2p 背景介绍: Wifi P2P (peer to peer):义为 Wifi 点对点,也叫 Wifi 直连(Wifi Direct),他是 Wifi Display(投屏) 应用的技术基础。 官方描述: 使用 WLAN 直连 (P2P) 技术,可以让具备相应硬件的 Android 4.0(API 级别 14)或更高版本设备在没有中间接入点的情况下,通过 WLAN 进行直接互联。使用这些 API,您可以实现支持 WLAN P2P 的设备间相互发现和连接,从而获得比蓝牙连接更远距离的高速连接通信效果。对于多人游戏或照片共享等需要在用户之间共享数据的应用而言,这一技术非常有用。 总结以下优点: 1、有比蓝牙更远的传输距离。未测试 2、有比蓝牙更快速的数据传输速度,更大的带宽。未测试 3、只需要打开 Wifi 即可,不需要加入任何网络或 AP,即可实现对等点连接通讯。 可实现通过 Wifi 连接,同时使用数据网络的场景,比喻:手机遥控无人机的同时,无人机需要访问远程服务器上传数据。
Wifi P2P 架构: 虽然上面提到两台或多台 Android 设备通过 Wifi P2P 通讯时不需要加入任何网络,但是 Wifi P2P 协议还是需要组件网络才能发现对方并建立 TCP 连接通讯的。在组网和通讯阶段一共有 3 个角色: 1、P2P Group Owner,或称为群主,充当服务端,并需要创建 ServerSocket 等待客户端的连接,获得 IO 流与客户端通讯或转发消息给其他客户端。 2、P2P Client,或称为组员,充当客户端,需要创建 Socket 与服务器通讯。 3、P2P Device,在上面的过程中,服务器端和客户端都是一个独立的设备,拥有唯一的设备特征信息。 4、广播接收器: WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION:检查 Wi-Fi P2P 是否已启用。Android 4.0 以上系统才有此功能。 WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION:对等设备发生变化,一般是在调用 discoverPeers 方法后发送此广播。在此广播中,你可以调用 requestPeers 方法,获得扫描到的对等设备列表。 WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION:连接状态发生变化,一般在调用 connect 或 cancelConnect 方法时会发送此广播。状态共有 5 种:WifiP2pDevice.AVAILABLE、WifiP2pDevice.INVITED、WifiP2pDevice.CONNECTED、WifiP2pDevice.FAILED 和 WifiP2pDevice.UNAVAILABLE 。 当判断连接信息为连接状态时,即 networkInfo.isConnected() ,你应当继续请求连接的具体信息 mManager.requestConnectionInfo(...),然后获得群主的详细设备信息,建立 Socket 通讯。 WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:此设备的WiFi状态更改回调,应用可使用 requestDeviceInfo() 来检索当前连接信息。
在未组网之前,是不存在群主、组员之称的。只有在设备尝试发现并连接对方时,系统才会通过 P2P 协议尝试使多端设备组件为一个群组,并自动确定某一个设备为群主。但是本人在实测过程中发现,是需要先有群主,才会加入组员组网通讯的。 更底层的原理参考:https://blog.csdn.net/wirelessdisplay/article/details/53365377 连接流程: 绘制了一张流程图,描述我 Demo 的连接过程。 服务端流程:
客户端流程: Android 代码层面介绍: 1、设置相关权限: 因为在建立 P2P 连接后,需要建立 Socket 通讯,所以需要 INTERNET 权限。
2、注册广播,有 4 个核心广播 Action: @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { // Check to see if Wi-Fi is enabled and notify appropriate activity // 检查 Wi-Fi P2P 是否已启用 int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1); boolean isEnabled = (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED); if (mWifiP2PListener != null) { mWifiP2PListener.onWifiP2pEnabled(isEnabled); } } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { // Call WifiP2pManager.requestPeers() to get a list of current peers WifiP2pDeviceList wifiP2pDeviceList = intent.getParcelableExtra(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); if (mWifiP2PListener != null && wifiP2pDeviceList != null) { mWifiP2PListener.onPeersAvailable(wifiP2pDeviceList.getDeviceList()); } // 异步方法 WifiP2PHelper.getInstance(context).requestPeers(); } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { // Respond to new connection or disconnections // 链接状态变化回调 // 此广播 会和 WIFI_P2P_THIS_DEVICE_CHANGED_ACTION 同时回调 // 注册广播、连接成功、连接失败 三种时机都会调用 // 应用可使用 requestConnectionInfo()、requestNetworkInfo() 或 requestGroupInfo() 来检索当前连接信息。 NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO); if (networkInfo != null && networkInfo.isConnected() && mManager != null && mWifiP2PListener != null) { WifiP2PHelper.getInstance(context).requestConnectInfo(); } else { if (mWifiP2PListener != null) { mWifiP2PListener.onConnectionInfoAvailable(null); } } } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) { // Respond to this device's wifi state changing // 此设备的WiFi状态更改回调 // 此广播 会和 WIFI_P2P_CONNECTION_CHANGED_ACTION 同时回调 // 注册广播、连接成功、连接失败 三种时机都会调用 // 应用可使用 requestDeviceInfo() 来检索当前连接信息。 WifiP2pDevice wifiP2pDevice = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE); if (mWifiP2PListener != null) { mWifiP2PListener.onSelfDeviceAvailable(wifiP2pDevice); } } }
3、初始化 Wifi P2P: private WifiP2PHelper(Context context) { if (context == null || context.getApplicationContext() == null) { throw new IllegalArgumentException("context is null exception."); } mApplicationContext = context.getApplicationContext(); mManager = (WifiP2pManager) mApplicationContext.getSystemService(Context.WIFI_P2P_SERVICE); // 将此应用注册到 WLAN P2P 框架 mChannel = mManager.initialize(mApplicationContext, Looper.getMainLooper(), null); mReceiver = new WifiP2PBroadCastReceiver(mManager, mChannel); }初始化阶段的核心就是调用 manager.initialize(...) 方法,得到群组内通讯的通道对象 channel。
4、服务端创建群组: public void createGroup() { mManager.createGroup(mChannel, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { if (mWifiP2PListener != null) { mWifiP2PListener.onCreateGroup(true); } } @Override public void onFailure(int reason) { if (mWifiP2PListener != null) { mWifiP2PListener.onCreateGroup(false); } } }); }5、客户端扫描对等设备: public void discover() { mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { if (mWifiP2PListener != null) { mWifiP2PListener.onDiscoverPeers(true); } } @Override public void onFailure(int reason) { if (mWifiP2PListener != null) { mWifiP2PListener.onDiscoverPeers(false); } } }); }6、客户端收到扫描成功广播后,请求扫描到的结果。尝试了解设备,获取到连接广播,在连接成功时,请求连接的详细信息,获取服务端的 IP 地址,建立 Socket 连接。 } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { // Call WifiP2pManager.requestPeers() to get a list of current peers WifiP2pDeviceList wifiP2pDeviceList = intent.getParcelableExtra(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); if (mWifiP2PListener != null && wifiP2pDeviceList != null) { mWifiP2PListener.onPeersAvailable(wifiP2pDeviceList.getDeviceList()); } // 异步方法 WifiP2PHelper.getInstance(context).requestPeers(); } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { // Respond to new connection or disconnections // 链接状态变化回调 // 此广播 会和 WIFI_P2P_THIS_DEVICE_CHANGED_ACTION 同时回调 // 注册广播、连接成功、连接失败 三种时机都会调用 // 应用可使用 requestConnectionInfo()、requestNetworkInfo() 或 requestGroupInfo() 来检索当前连接信息。 NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO); if (networkInfo != null && networkInfo.isConnected() && mManager != null && mWifiP2PListener != null) { WifiP2PHelper.getInstance(context).requestConnectInfo(); } else { if (mWifiP2PListener != null) { mWifiP2PListener.onConnectionInfoAvailable(null); } } }7、服务器创建 ServerSocket 接收客户端,并死循环读取 InputStream 数据: private void initSocket() { try { serverSocket = new ServerSocket(SERVER_PORT); // 需要设置为无限超时 // serverSocket.setSoTimeout(10000); while (!isQuitReadClient) { socket = serverSocket.accept(); inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); String text = "连接到客户端 -> " + PHONE_INFO; IOHelper.writeText(outputStream, text); } } catch (Exception e) { e.printStackTrace(); } } private void receive() { try { while (!isQuitReadMessage) { Thread.sleep(500); String text = IOHelper.readText(inputStream); if (TextUtils.isEmpty(text)) { continue; } postToUI(text); } } catch (Exception e) { e.printStackTrace(); } }8、客户端床架 Socket 连接服务端,并死循环读取 InputStream: private void initSocket() { try { socket = new Socket(serverIP, SERVER_PORT); inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); String text = "连接到服务端 -> " + PHONE_INFO; IOHelper.writeText(outputStream, text); } catch (IOException e) { e.printStackTrace(); } } private void receive() { try { while (!isQuitReadMessage) { Thread.sleep(500); String text = IOHelper.readText(inputStream); if (TextUtils.isEmpty(text)) { continue; } postToUI(text); } } catch (Exception e) { e.printStackTrace(); } }Demo 效果图: 服务端:
客户端: 服务端接收连接确认对话框: 坑点: 1、需要先创建群组,客户端才可以连接并加入组,否则连接不上。 2、服务端 ServerSocket 在等待时不要设置超时,否则遇到客户端连不上问题时难以排查。 3、如果服务端退出时没有移除群组,或客户端退出时没有断开连接,在下次连接时会出现连不上问题,不确定原因。 4、客户端首次连接服务端时,服务端会弹出请求对话框。服务端同意后才会建立连接。
Demo 下载地址:https://github.com/mengzhinan/WiFi_P2P_test
|
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |